Bug 1675127 - Remove code from ASRouter that shows the triplets (3 cards) at the top of the newtab page r=k88hudson,pdahiya,fluent-reviewers,flod

Differential Revision: https://phabricator.services.mozilla.com/D97213
This commit is contained in:
Andrei Oprea 2020-12-03 13:47:39 +00:00
parent 96a85ab8c6
commit 199f6ebb12
32 changed files with 502 additions and 3831 deletions

View File

@ -65,7 +65,6 @@ function templateHTML(options) {
<link rel="stylesheet" href="chrome://activity-stream/content/css/activity-stream.css" />
</head>
<body class="activity-stream">
<div id="header-asrouter-container" role="presentation"></div>
<div id="root"></div>
<div id="footer-asrouter-container" role="presentation"></div>${
options.noscripts ? "" : scriptRender

View File

@ -12,9 +12,7 @@ import { NEWTAB_DARK_THEME } from "content-src/lib/constants";
import React from "react";
import ReactDOM from "react-dom";
import { SnippetsTemplates } from "./templates/template-manifest";
import { FirstRun } from "./templates/FirstRun/FirstRun";
const TEMPLATES_ABOVE_PAGE = ["extended_triplets"];
const TEMPLATES_BELOW_SEARCH = ["simple_below_search_snippet"];
// Note: nextProps/prevProps refer to props passed to <ImpressionsWrapper />, not <ASRouterUISurface />
@ -40,9 +38,6 @@ export class ASRouterUISurface extends React.PureComponent {
this.state = { message: {} };
if (props.document) {
this.headerPortal = props.document.getElementById(
"header-asrouter-container"
);
this.footerPortal = props.document.getElementById(
"footer-asrouter-container"
);
@ -169,16 +164,6 @@ export class ASRouterUISurface extends React.PureComponent {
}
clearMessage(id) {
// Request new set of dynamic triplet cards when click on a card CTA clear
// message and 'id' matches one of the cards in message bundle
if (
this.state.message &&
this.state.message.bundle &&
this.state.message.bundle.find(card => card.id === id)
) {
this.requestMessage();
}
if (id === this.state.message.id) {
this.setState({ message: {} });
}
@ -323,35 +308,6 @@ export class ASRouterUISurface extends React.PureComponent {
);
}
renderFirstRun() {
const { message } = this.state;
if (TEMPLATES_ABOVE_PAGE.includes(message.template)) {
return (
<ImpressionsWrapper
id="FIRST_RUN"
message={this.state.message}
sendImpression={this.sendImpression}
shouldSendImpressionOnUpdate={shouldSendImpressionOnUpdate}
// This helps with testing
document={this.props.document}
>
<FirstRun
document={this.props.document}
message={message}
sendUserActionTelemetry={this.sendUserActionTelemetry}
executeAction={ASRouterUtils.executeAction}
onBlockById={ASRouterUtils.blockById}
onDismiss={this.onDismiss}
fxaEndpoint={this.props.fxaEndpoint}
appUpdateChannel={this.props.appUpdateChannel}
fetchFlowParams={this.fetchFlowParams}
/>
</ImpressionsWrapper>
);
}
return null;
}
render() {
const { message } = this.state;
if (!message.id) {
@ -360,9 +316,6 @@ export class ASRouterUISurface extends React.PureComponent {
const shouldRenderBelowSearch = TEMPLATES_BELOW_SEARCH.includes(
message.template
);
const shouldRenderInHeader = TEMPLATES_ABOVE_PAGE.includes(
message.template
);
return shouldRenderBelowSearch ? (
// Render special below search snippets in place;
@ -370,15 +323,14 @@ export class ASRouterUISurface extends React.PureComponent {
{this.renderSnippets()}
</div>
) : (
// For onboarding, regular snippets etc. we should render
// everything in our footer container.
// For regular snippets etc. we should render everything in our footer
// container.
ReactDOM.createPortal(
<>
{this.renderPreviewBanner()}
{this.renderFirstRun()}
{this.renderSnippets()}
</>,
shouldRenderInHeader ? this.headerPortal : this.footerPortal
this.footerPortal
)
);
}

View File

@ -21,24 +21,11 @@ export class ModalOverlayWrapper extends React.PureComponent {
componentWillMount() {
this.props.document.addEventListener("keydown", this.onKeyDown);
this.props.document.body.classList.add("modal-open");
this.header = this.props.document.getElementById(
"header-asrouter-container"
);
if (this.header) {
this.props.document.getElementById("root").classList.add("modal-height");
}
}
componentWillUnmount() {
this.props.document.removeEventListener("keydown", this.onKeyDown);
this.props.document.body.classList.remove("modal-open");
if (this.header) {
this.props.document
.getElementById("root")
.classList.remove("modal-height");
}
}
render() {
@ -59,13 +46,6 @@ export class ModalOverlayWrapper extends React.PureComponent {
id={props.id}
role="dialog"
>
{props.hasDismissIcon && (
<button
className="icon icon-dismiss"
onClick={props.onClose}
data-l10n-id="onboarding-cards-dismiss"
/>
)}
{props.children}
</div>
</div>
@ -74,24 +54,3 @@ export class ModalOverlayWrapper extends React.PureComponent {
}
ModalOverlayWrapper.defaultProps = { document: global.document };
export class ModalOverlay extends React.PureComponent {
render() {
const { title, button_label } = this.props;
return (
<ModalOverlayWrapper onClose={this.props.onDismissBundle}>
<h2> {title} </h2>
{this.props.children}
<div className="footer">
<button
className="button primary modalButton"
onClick={this.props.onDismissBundle}
>
{" "}
{button_label}{" "}
</button>
</div>
</ModalOverlayWrapper>
);
}
}

View File

@ -23,12 +23,6 @@ $modal-scrollbar-z-index: 1100;
}
}
.modal-height {
// "Welcome header" has 40px of padding and 36px font size that get neglected using position absolute
// causing this to visually collide with the newtab searchbar
padding-top: 80px;
}
.modalOverlayInner {
min-width: min-content;
width: 100%;
@ -54,23 +48,6 @@ $modal-scrollbar-z-index: 1100;
display: block;
}
.icon-dismiss {
border: 0;
cursor: pointer;
inset-inline-end: 0;
padding: 20px;
fill: $white;
position: absolute;
&:hover {
background-color: $trailhead-purple;
}
&:focus {
border: 1px dotted;
}
}
h2 {
color: $grey-60;
text-align: center;

View File

@ -7,16 +7,3 @@ First run help onboard new users by showing relevant messaging on about:welcome
A full-page multistep experience that shows up on first run since Fx80 with browser.aboutwelcome.enabled pref as true.
Setting browser.aboutwelcome.enabled to false make first run looks like about:newtab and hides about:welcome
## Triplets
The cards that show up above the new tab content on the first instance of new tab. Setting browser.aboutwelcome.enabled to false and trailhead.firstrun.newtab.triplets to one of values below hides multistage welcome and takes user straight to triplets on opening about:welcome page
* supercharge - Shows Sync, Monitor and Mobile onboarding cards. Supported in 71+.
* payoff - Shows Monitor, Facbook Container and Firefox Send onboarding cards. Supported in 71 only.
* mutidevice - Shows Pocket, Send Tabs and Mobile onboarding cards. Supported in 71 only.
* privacy - Shows Private Browsing, Tracking Protection and Lockwise. Supported in 71 only.
In 72+
* static - same experience as supercharge triplet - with Sync, Monitor and Mobile onboarding cards
* dynamic - Dynamic triplets showing three onboarding cards (Sync, Monitor and Private Browsing) that gets swapped with preselected list of cards that satisfies targeting rules. Preselected cards supported are Send Tab, Mobile and Lockwise.
* dynamic_chrome - Dynamic triplets showing three onboarding cards (Chrome switchers, Sync and Monitor) that gets swapped with preselected list of cards that satisfies targeting rules. Preselected cards supported are Private Browsing, Send Tab, Mobile and Lockwise.

View File

@ -28,7 +28,6 @@ Please note that some targeting attributes require stricter controls on the tele
* [sync](#sync)
* [topFrecentSites](#topfrecentsites)
* [totalBookmarksCount](#totalbookmarkscount)
* [trailheadTriplet](#trailheadtriplet)
* [usesFirefoxSync](#usesfirefoxsync)
* [isFxAEnabled](#isFxAEnabled)
* [xpinstallEnabled](#xpinstallEnabled)
@ -443,10 +442,6 @@ Total number of bookmarks.
declare const totalBookmarksCount: number;
```
### `trailheadTriplet`
(67.05+ only) Experiment branch for "triplet" study
### `usesFirefoxSync`
Does the user use Firefox sync?

View File

@ -1,129 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from "react";
import { Triplets } from "./Triplets";
import { BASE_PARAMS } from "./addUtmParams";
// Note: should match the transition time on .trailheadCards in _Trailhead.scss
const TRANSITION_LENGTH = 500;
export const FLUENT_FILES = [
"branding/brand.ftl",
"browser/branding/brandings.ftl",
"browser/branding/sync-brand.ftl",
"browser/newtab/onboarding.ftl",
];
export const helpers = {
addFluent(document) {
FLUENT_FILES.forEach(file => {
const link = document.head.appendChild(document.createElement("link"));
link.href = file;
link.rel = "localization";
});
},
};
export class FirstRun extends React.PureComponent {
constructor(props) {
super(props);
this.didLoadFlowParams = false;
this.state = {
didUserClearTriplets: false,
flowParams: undefined,
};
this.closeTriplets = this.closeTriplets.bind(this);
helpers.addFluent(this.props.document);
// Update utm campaign parameters by appending channel for
// differentiating campaign in amplitude
if (this.props.appUpdateChannel) {
BASE_PARAMS.utm_campaign += `-${this.props.appUpdateChannel}`;
}
}
get UTMTerm() {
const { message } = this.props;
let UTMTerm = message.utm_term || "";
UTMTerm =
message.utm_term && message.trailheadTriplet
? `${message.utm_term}-${message.trailheadTriplet}`
: UTMTerm;
return UTMTerm;
}
async fetchFlowParams() {
const { fxaEndpoint, fetchFlowParams } = this.props;
if (fxaEndpoint && this.UTMTerm && !this.didLoadFlowParams) {
this.didLoadFlowParams = true;
const flowParams = await fetchFlowParams({
...BASE_PARAMS,
entrypoint: "activity-stream-firstrun",
form_type: "email",
utm_term: this.UTMTerm,
});
this.setState({ flowParams });
}
}
componentDidMount() {
this.fetchFlowParams();
}
componentDidUpdate() {
// In case we didn't have FXA info immediately, try again when we receive it.
this.fetchFlowParams();
}
closeTriplets() {
this.setState({ didUserClearTriplets: true });
// Closing triplets should prevent any future extended triplets from showing up
setTimeout(() => {
this.props.onBlockById("EXTENDED_TRIPLETS_1");
}, TRANSITION_LENGTH);
}
render() {
const { props, state, UTMTerm } = this;
const { sendUserActionTelemetry, executeAction, message } = props;
const { didUserClearTriplets, flowParams } = state;
const hasTriplets = Boolean(message.bundle && message.bundle.length);
const triplets = hasTriplets ? message.bundle : null;
const isTripletsContainerVisible = hasTriplets && !didUserClearTriplets;
// Allow 1) falsy to not render a header 2) default welcome header 3) custom header
const tripletsHeaderId =
message.tripletsHeaderId === undefined
? "onboarding-welcome-header"
: message.tripletsHeaderId;
return (
<>
{hasTriplets ? (
<Triplets
document={props.document}
cards={triplets}
headerId={tripletsHeaderId}
showCardPanel={isTripletsContainerVisible}
hideContainer={this.closeTriplets}
sendUserActionTelemetry={sendUserActionTelemetry}
UTMTerm={`${UTMTerm}-card`}
flowParams={flowParams}
onAction={executeAction}
onBlockById={props.onBlockById}
/>
) : null}
</>
);
}
}

View File

@ -1,96 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
import React from "react";
import { OnboardingCard } from "../../templates/OnboardingMessage/OnboardingMessage";
import { addUtmParams } from "./addUtmParams";
export class Triplets extends React.PureComponent {
constructor(props) {
super(props);
this.onCardAction = this.onCardAction.bind(this);
this.onHideContainer = this.onHideContainer.bind(this);
}
componentWillMount() {
global.document.body.classList.add("inline-onboarding");
}
componentWillUnmount() {
this.props.document.body.classList.remove("inline-onboarding");
}
onCardAction(action, message) {
let actionUpdates = {};
const { flowParams, UTMTerm } = this.props;
if (action.type === "OPEN_URL") {
let url = new URL(action.data.args);
addUtmParams(url, UTMTerm);
if (action.addFlowParams) {
url.searchParams.append("device_id", flowParams.deviceId);
url.searchParams.append("flow_id", flowParams.flowId);
url.searchParams.append("flow_begin_time", flowParams.flowBeginTime);
}
actionUpdates = { data: { ...action.data, args: url.toString() } };
}
this.props.onAction({ ...action, ...actionUpdates });
// Only block if message is in dynamic triplets experiment
if (message.blockOnClick) {
this.props.onBlockById(message.id, { preloadedOnly: true });
}
}
onHideContainer() {
const { sendUserActionTelemetry, cards, hideContainer } = this.props;
hideContainer();
sendUserActionTelemetry({
event: "DISMISS",
id: "onboarding-cards",
message_id: cards.map(m => m.id).join(","),
action: "onboarding_user_event",
});
}
render() {
const {
cards,
headerId,
showCardPanel,
sendUserActionTelemetry,
} = this.props;
return (
<div
className={`trailheadCards ${showCardPanel ? "expanded" : "collapsed"}`}
>
<div className="trailheadCardsInner" aria-hidden={!showCardPanel}>
{headerId && <h1 data-l10n-id={headerId} />}
<div className={`trailheadCardGrid${showCardPanel ? " show" : ""}`}>
{cards.map(card => (
<OnboardingCard
key={card.id}
message={card}
className="trailheadCard"
sendUserActionTelemetry={sendUserActionTelemetry}
onAction={this.onCardAction}
UISurface="TRAILHEAD"
{...card}
/>
))}
</div>
{showCardPanel && (
<button
className="icon icon-dismiss"
onClick={this.onHideContainer}
data-l10n-id="onboarding-cards-dismiss"
/>
)}
</div>
</div>
);
}
}

View File

@ -1,215 +0,0 @@
.trailheadCards {
background: var(--trailhead-cards-background-color);
overflow: hidden;
text-align: center;
// Note: should match TRANSITION_LENGTH in FirstRun.jsx
transition: max-height 0.5s $photon-easing;
// This is needed for the transition to work, but will cut off content at the smallest breakpoint
@media (min-width: $break-point-medium) {
max-height: 1000px;
}
&.collapsed {
max-height: 0;
}
h1 {
font-size: 36px;
font-weight: 200;
margin: 0 0 40px;
color: var(--trailhead-header-text-color);
}
}
.trailheadCardsInner {
margin: auto;
padding: 40px $section-horizontal-padding;
@media (min-width: $break-point-medium) {
width: $wrapper-max-width-medium;
}
@media (min-width: $break-point-large) {
width: $wrapper-max-width-large;
}
@media (min-width: $break-point-widest) {
width: $wrapper-max-width-widest;
}
.icon-dismiss {
border: 0;
border-radius: 4px;
cursor: pointer;
inset-inline-end: 15px;
padding: 15px;
opacity: 0.75;
position: absolute;
top: 15px;
&:hover,
&:focus {
background-color: var(--newtab-element-active-color);
}
}
}
.trailheadCardGrid {
display: grid;
grid-gap: $base-gutter;
margin: 0;
opacity: 0;
transition: opacity 0.4s;
transition-delay: 0.1s;
grid-auto-rows: 1fr;
&.show {
opacity: 1;
}
@media (min-width: $break-point-medium) {
grid-template-columns: repeat(auto-fit, $card-width);
}
@media (min-width: $break-point-widest) {
grid-template-columns: repeat(auto-fit, $card-width-large);
}
}
.trailheadCard {
position: relative;
background: var(--newtab-card-background-color);
border-radius: 4px;
box-shadow: var(--newtab-card-shadow);
font-size: 13px;
padding: 20px 20px 60px;
@media (max-width: 865px) {
padding: 20px;
}
@media (min-width: $break-point-widest) {
font-size: 15px;
}
.onboardingTitle {
font-weight: normal;
color: var(--newtab-text-primary-color);
margin: 10px 0 4px;
font-size: 15px;
@media (min-width: $break-point-widest) {
font-size: 18px;
}
}
.onboardingText {
margin: 0 0 60px;
color: var(--newtab-text-conditional-color);
line-height: 1.5;
font-weight: 200;
}
.onboardingButton {
color: var(--newtab-text-conditional-color);
background: var(--trailhead-card-button-background-color);
border: 0;
margin: 14px;
min-width: 70%;
padding: 6px 14px;
white-space: pre-wrap;
&:focus,
&:hover {
box-shadow: none;
background: var(--trailhead-card-button-background-hover-color);
}
&:focus {
outline: dotted 1px;
}
&:active {
background: var(--trailhead-card-button-background-active-color);
}
}
.onboardingButtonContainer {
position: absolute;
bottom: 16px;
left: 0;
width: 100%;
text-align: center;
}
}
.activity-stream.welcome {
overflow: hidden;
}
.inline-onboarding {
&.activity-stream.welcome {
overflow-y: hidden;
}
.outer-wrapper {
position: relative;
display: block;
.prefs-button {
button {
position: absolute;
}
}
}
.asrouter-toggle {
position: absolute;
}
}
.error {
display: none;
}
.error.active {
display: block;
padding: 5px 12px;
animation: fade-down 450ms;
font-size: 12px;
font-weight: 500;
color: $white;
background-color: $red-60;
position: absolute;
inset-inline-start: 50px;
top: -28px;
border-radius: 2px;
&::before {
inset-inline-start: 12px;
background: $red-60;
bottom: -8px;
content: '.';
height: 16px;
position: absolute;
text-indent: -999px;
transform: rotate(45deg);
white-space: nowrap;
width: 16px;
z-index: -1;
}
}
@keyframes fade-down {
0% {
opacity: 0;
transform: translateY(-15px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

View File

@ -5,7 +5,6 @@
import { actionCreators as ac, actionTypes as at } from "common/Actions.jsm";
import { ASRouterUtils } from "../../asrouter/asrouter-utils";
import { connect } from "react-redux";
import { ModalOverlay } from "../../asrouter/components/ModalOverlay/ModalOverlay";
import React from "react";
import { SimpleHashRouter } from "./SimpleHashRouter";
@ -489,9 +488,6 @@ export class ASRouterAdminInner extends React.PureComponent {
this.handleClearAllImpressionsByProvider = this.handleClearAllImpressionsByProvider.bind(
this
);
this.findOtherBundledMessagesOfSameTemplate = this.findOtherBundledMessagesOfSameTemplate.bind(
this
);
this.handleExpressionEval = this.handleExpressionEval.bind(this);
this.onChangeTargetingParameters = this.onChangeTargetingParameters.bind(
this
@ -501,7 +497,6 @@ export class ASRouterAdminInner extends React.PureComponent {
);
this.setAttribution = this.setAttribution.bind(this);
this.onCopyTargetingParams = this.onCopyTargetingParams.bind(this);
this.onPasteTargetingParams = this.onPasteTargetingParams.bind(this);
this.onNewTargetingParams = this.onNewTargetingParams.bind(this);
this.handleUpdateWNMessages = this.handleUpdateWNMessages.bind(this);
this.handleForceWNP = this.handleForceWNP.bind(this);
@ -521,11 +516,9 @@ export class ASRouterAdminInner extends React.PureComponent {
collapsedMessages: [],
modifiedMessages: [],
evaluationStatus: {},
trailheadTriplet: "",
stringTargetingParameters: null,
newStringTargetingParameters: null,
copiedToClipboard: false,
pasteFromClipboard: false,
attributionParameters: {
source: "addons.mozilla.org",
medium: "referral",
@ -572,27 +565,11 @@ export class ASRouterAdminInner extends React.PureComponent {
}).then(this.setStateFromParent);
}
findOtherBundledMessagesOfSameTemplate(template) {
return this.state.messages.filter(
msg => msg.template === template && msg.bundled
);
}
handleBlock(msg) {
if (msg.bundled && msg.template !== "onboarding") {
// If we are blocking a message that belongs to a bundle, block all other messages that are bundled of that same template
let bundle = this.findOtherBundledMessagesOfSameTemplate(msg.template);
return () => ASRouterUtils.blockBundle(bundle);
}
return () => ASRouterUtils.blockById(msg.id);
}
handleUnblock(msg) {
if (msg.bundled && msg.template !== "onboarding") {
// If we are unblocking a message that belongs to a bundle, unblock all other messages that are bundled of that same template
let bundle = this.findOtherBundledMessagesOfSameTemplate(msg.template);
return () => ASRouterUtils.unblockBundle(bundle);
}
return () => ASRouterUtils.unblockById(msg.id);
}
@ -827,14 +804,6 @@ export class ASRouterAdminInner extends React.PureComponent {
document.execCommand("copy");
}
// Copy all clipboard data to targeting parameters
onPasteTargetingParams(event) {
this.setState(({ pasteFromClipboard }) => ({
pasteFromClipboard: !pasteFromClipboard,
newStringTargetingParameters: "",
}));
}
onNewTargetingParams(event) {
this.setState({ newStringTargetingParameters: event.target.value });
event.target.classList.remove("errorState");
@ -1324,35 +1293,6 @@ export class ASRouterAdminInner extends React.PureComponent {
);
}
renderPasteModal() {
if (!this.state.pasteFromClipboard) {
return null;
}
const errors =
this.refs.targetingParamsEval &&
this.refs.targetingParamsEval.innerText.length;
return (
<ModalOverlay
innerStyle="pasteModal"
title="New targeting parameters"
button_label={errors ? "Cancel" : "Done"}
onDismissBundle={this.onPasteTargetingParams}
>
<div className="onboardingMessage">
<p>
<textarea
onChange={this.onNewTargetingParams}
value={this.state.newStringTargetingParameters}
rows="20"
cols="60"
/>
</p>
<p ref="targetingParamsEval" />
</div>
</ModalOverlay>
);
}
renderTargetingParameters() {
// There was no error and the result is truthy
const success =
@ -1412,13 +1352,6 @@ export class ASRouterAdminInner extends React.PureComponent {
? "Parameters copied!"
: "Copy parameters"}
</button>
<button
className="ASRouterButton secondary"
onClick={this.onPasteTargetingParams}
disabled={this.state.pasteFromClipboard}
>
Paste parameters
</button>
</td>
</tr>
{this.state.stringTargetingParameters &&
@ -1686,19 +1619,6 @@ export class ASRouterAdminInner extends React.PureComponent {
return <p>No errors</p>;
}
renderTrailheadInfo() {
return (
<table className="minimal-table">
<tbody>
<tr>
<td>Triplet branch</td>
<td>{this.state.trailheadTriplet}</td>
</tr>
</tbody>
</table>
);
}
renderWNPTests() {
if (!this.state.messages) {
return null;
@ -1858,12 +1778,9 @@ export class ASRouterAdminInner extends React.PureComponent {
</button>
</h2>
{this.state.providers ? this.renderProviders() : null}
<h2>Trailhead</h2>
{this.renderTrailheadInfo()}
<h2>Messages</h2>
{this.renderMessageFilter()}
{this.renderMessages()}
{this.renderPasteModal()}
</React.Fragment>
);
}

View File

@ -176,6 +176,4 @@ input {
@import '../asrouter/templates/SimpleBelowSearchSnippet/SimpleBelowSearchSnippet';
@import '../asrouter/templates/SimpleSnippet/SimpleSnippet';
@import '../asrouter/templates/SubmitFormSnippet/SubmitFormSnippet';
@import '../asrouter/templates/OnboardingMessage/OnboardingMessage';
@import '../asrouter/templates/FirstRun/Triplets';
@import '../asrouter/templates/EOYSnippet/EOYSnippet';

View File

@ -93,13 +93,6 @@ body {
--newtab-snippets-background-color: #{$white};
--newtab-snippets-hairline-color: transparent;
// Trailhead
--trailhead-header-text-color: #{$trailhead-purple};
--trailhead-cards-background-color: #{$grey-20};
--trailhead-card-button-background-color: #{$grey-90-10};
--trailhead-card-button-background-hover-color: #{$grey-90-20};
--trailhead-card-button-background-active-color: #{$grey-90-30};
&[lwt-newtab-brighttext] {
// General styles
--newtab-background-color: #{$grey-80};
@ -171,12 +164,5 @@ body {
// Snippets
--newtab-snippets-background-color: #{$grey-70};
--newtab-snippets-hairline-color: #{$white-10};
// Trailhead
--trailhead-header-text-color: #{$white-60};
--trailhead-cards-background-color: #{$grey-90-10};
--trailhead-card-button-background-color: #{$grey-90-30};
--trailhead-card-button-background-hover-color: #{$grey-90-50};
--trailhead-card-button-background-active-color: #{$grey-90-70};
}
}

View File

@ -88,11 +88,6 @@ $about-welcome-gradient: linear-gradient(to bottom, $blue-70 40%, $aw-extra-blue
$about-welcome-extra-links: #676F7E;
$firefox-wordmark-default-color: #363959;
$firefox-wordmark-darktheme-color: $white;
$trailhead-violet: #7542E5;
$trailhead-purple: #2B2156;
$trailhead-purple-80: #36296D;
$trailhead-blue-60: #0250BB;
$trailhead-blue-70: #054096;
// New new tab experience colors.
$newtab-background-button-default-color: rgba(223, 223, 223, 0.5);

View File

@ -79,12 +79,7 @@ body {
--newtab-card-placeholder-color: #D7D7DB;
--newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
--newtab-snippets-background-color: #FFF;
--newtab-snippets-hairline-color: transparent;
--trailhead-header-text-color: #2B2156;
--trailhead-cards-background-color: #EDEDF0;
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.1);
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.2);
--trailhead-card-button-background-active-color: rgba(12, 12, 13, 0.3); }
--newtab-snippets-hairline-color: transparent; }
body[lwt-newtab-brighttext] {
--newtab-background-color: #2A2A2E;
--newtab-border-primary-color: rgba(249, 249, 250, 0.8);
@ -137,12 +132,7 @@ body {
--newtab-card-placeholder-color: #4A4A4F;
--newtab-card-shadow: 0 1px 8px 0 rgba(12, 12, 13, 0.2);
--newtab-snippets-background-color: #38383D;
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1);
--trailhead-header-text-color: rgba(255, 255, 255, 0.6);
--trailhead-cards-background-color: rgba(12, 12, 13, 0.1);
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.3);
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.5);
--trailhead-card-button-background-active-color: rgba(12, 12, 13, 0.7); }
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1); }
.icon {
background-position: center center;
@ -3581,9 +3571,6 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.modalOverlayOuter.active {
display: flex; }
.modal-height {
padding-top: 80px; }
.modalOverlayInner {
min-width: min-content;
width: 100%;
@ -3604,17 +3591,6 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
border-radius: 0; } }
.modalOverlayInner.active {
display: block; }
.modalOverlayInner .icon-dismiss {
border: 0;
cursor: pointer;
inset-inline-end: 0;
padding: 20px;
fill: #FFF;
position: absolute; }
.modalOverlayInner .icon-dismiss:hover {
background-color: #2B2156; }
.modalOverlayInner .icon-dismiss:focus {
border: 1px dotted; }
.modalOverlayInner h2 {
color: #4A4A4F;
text-align: center;
@ -3987,305 +3963,6 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.submissionStatus .submitStatusTitle {
font-size: 20px; }
.onboardingMessageImage.addons {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-addons@2x.png"); }
.onboardingMessageImage.privatebrowsing {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-privatebrowsing@2x.png"); }
.onboardingMessageImage.screenshots {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-screenshots@2x.png"); }
.onboardingMessageImage.gift {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-gift@2x.png"); }
.onboardingMessageImage.sync {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-sync@2x.png"); }
.onboardingMessageImage.devices {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-devices.svg"); }
.onboardingMessageImage.fbcont {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-fbcont.svg"); }
.onboardingMessageImage.import {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-import.svg"); }
.onboardingMessageImage.ffmonitor {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffmonitor.svg"); }
.onboardingMessageImage.ffsend {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffsend.svg"); }
.onboardingMessageImage.lockwise {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-lockwise.svg"); }
.onboardingMessageImage.mobile {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-mobile.svg"); }
.onboardingMessageImage.pledge {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pledge.svg"); }
.onboardingMessageImage.pocket {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pocket.svg"); }
.onboardingMessageImage.private {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-private.svg"); }
.onboardingMessageImage.sendtab {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-sendtab.svg"); }
.onboardingMessageImage.tracking {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-tracking.svg"); }
.onboardingMessage {
height: 340px;
text-align: center;
padding: 13px;
font-weight: 200; }
@media (max-width: 850px) {
.onboardingMessage {
height: 170px;
text-align: left;
padding: 10px;
border-bottom: 1px solid #D7D7DB;
display: flex;
margin-bottom: 11px; }
.onboardingMessage:last-child {
border: 0; }
.onboardingMessage .onboardingContent {
padding-left: 10px;
height: 100%; }
.onboardingMessage .onboardingContent > span > h3 {
margin-top: 0;
margin-bottom: 4px;
font-weight: 400; }
.onboardingMessage .onboardingContent > span > p {
margin-top: 0;
line-height: 22px;
font-size: 15px; } }
@media (max-width: 650px) {
.onboardingMessage {
height: 250px; } }
.onboardingMessage .onboardingContent {
height: 175px; }
.onboardingMessage .onboardingContent > span > h3 {
color: #0C0C0D;
margin-bottom: 8px;
font-weight: 400; }
.onboardingMessage .onboardingContent > span > p {
color: #4A4A4F;
margin-top: 0;
height: 180px;
margin-bottom: 12px;
font-size: 15px;
line-height: 22px; }
@media (max-width: 650px) {
.onboardingMessage .onboardingContent > span > p {
margin-bottom: 0;
height: 160px; } }
.onboardingMessage .onboardingButton {
background-color: rgba(12, 12, 13, 0.1);
border: 0;
width: 150px;
height: 30px;
margin-bottom: 23px;
padding: 4px 0 6px;
font-size: 15px; }
@media (max-width: 850px) {
.onboardingMessage .onboardingButton {
float: right;
margin-top: -105px;
margin-inline-end: -10px; } }
@media (max-width: 650px) {
.onboardingMessage .onboardingButton {
float: none; } }
.onboardingMessage .onboardingButton:focus, .onboardingMessage .onboardingButton.active, .onboardingMessage .onboardingButton:hover {
box-shadow: 0 0 0 5px #D7D7DB;
transition: box-shadow 150ms; }
.onboardingMessage::before {
content: '';
height: 230px;
width: 1px;
position: absolute;
background-color: #D7D7DB;
margin-top: 40px;
margin-inline-start: 215px; }
@media (max-width: 850px) {
.onboardingMessage::before {
content: none; } }
.onboardingMessage:last-child::before {
content: none; }
.onboardingMessageImage {
height: 112px;
width: 180px;
background-size: auto 140px;
background-position: center center;
background-repeat: no-repeat;
display: inline-block; }
@media (max-width: 865px) {
.onboardingMessageImage {
height: 75px;
min-width: 80px;
background-size: 140px; } }
.trailheadCards {
background: var(--trailhead-cards-background-color);
overflow: hidden;
text-align: center;
transition: max-height 0.5s cubic-bezier(0.07, 0.95, 0, 1); }
@media (min-width: 610px) {
.trailheadCards {
max-height: 1000px; } }
.trailheadCards.collapsed {
max-height: 0; }
.trailheadCards h1 {
font-size: 36px;
font-weight: 200;
margin: 0 0 40px;
color: var(--trailhead-header-text-color); }
.trailheadCardsInner {
margin: auto;
padding: 40px 25px; }
@media (min-width: 610px) {
.trailheadCardsInner {
width: 530px; } }
@media (min-width: 866px) {
.trailheadCardsInner {
width: 786px; } }
@media (min-width: 1122px) {
.trailheadCardsInner {
width: 1042px; } }
.trailheadCardsInner .icon-dismiss {
border: 0;
border-radius: 4px;
cursor: pointer;
inset-inline-end: 15px;
padding: 15px;
opacity: 0.75;
position: absolute;
top: 15px; }
.trailheadCardsInner .icon-dismiss:hover, .trailheadCardsInner .icon-dismiss:focus {
background-color: var(--newtab-element-active-color); }
.trailheadCardGrid {
display: grid;
grid-gap: 32px;
margin: 0;
opacity: 0;
transition: opacity 0.4s;
transition-delay: 0.1s;
grid-auto-rows: 1fr; }
.trailheadCardGrid.show {
opacity: 1; }
@media (min-width: 610px) {
.trailheadCardGrid {
grid-template-columns: repeat(auto-fit, 224px); } }
@media (min-width: 1122px) {
.trailheadCardGrid {
grid-template-columns: repeat(auto-fit, 309px); } }
.trailheadCard {
position: relative;
background: var(--newtab-card-background-color);
border-radius: 4px;
box-shadow: var(--newtab-card-shadow);
font-size: 13px;
padding: 20px 20px 60px; }
@media (max-width: 865px) {
.trailheadCard {
padding: 20px; } }
@media (min-width: 1122px) {
.trailheadCard {
font-size: 15px; } }
.trailheadCard .onboardingTitle {
font-weight: normal;
color: var(--newtab-text-primary-color);
margin: 10px 0 4px;
font-size: 15px; }
@media (min-width: 1122px) {
.trailheadCard .onboardingTitle {
font-size: 18px; } }
.trailheadCard .onboardingText {
margin: 0 0 60px;
color: var(--newtab-text-conditional-color);
line-height: 1.5;
font-weight: 200; }
.trailheadCard .onboardingButton {
color: var(--newtab-text-conditional-color);
background: var(--trailhead-card-button-background-color);
border: 0;
margin: 14px;
min-width: 70%;
padding: 6px 14px;
white-space: pre-wrap; }
.trailheadCard .onboardingButton:focus, .trailheadCard .onboardingButton:hover {
box-shadow: none;
background: var(--trailhead-card-button-background-hover-color); }
.trailheadCard .onboardingButton:focus {
outline: dotted 1px; }
.trailheadCard .onboardingButton:active {
background: var(--trailhead-card-button-background-active-color); }
.trailheadCard .onboardingButtonContainer {
position: absolute;
bottom: 16px;
left: 0;
width: 100%;
text-align: center; }
.activity-stream.welcome {
overflow: hidden; }
.inline-onboarding.activity-stream.welcome {
overflow-y: hidden; }
.inline-onboarding .outer-wrapper {
position: relative;
display: block; }
.inline-onboarding .outer-wrapper .prefs-button button {
position: absolute; }
.inline-onboarding .asrouter-toggle {
position: absolute; }
.error {
display: none; }
.error.active {
display: block;
padding: 5px 12px;
animation: fade-down 450ms;
font-size: 12px;
font-weight: 500;
color: #FFF;
background-color: #D70022;
position: absolute;
inset-inline-start: 50px;
top: -28px;
border-radius: 2px; }
.error.active::before {
inset-inline-start: 12px;
background: #D70022;
bottom: -8px;
content: '.';
height: 16px;
position: absolute;
text-indent: -999px;
transform: rotate(45deg);
white-space: nowrap;
width: 16px;
z-index: -1; }
@keyframes fade-down {
0% {
opacity: 0;
transform: translateY(-15px); }
100% {
opacity: 1;
transform: translateY(0); } }
.EOYSnippetForm {
margin: 10px 0 8px;
align-self: start;

View File

@ -82,12 +82,7 @@ body {
--newtab-card-placeholder-color: #D7D7DB;
--newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
--newtab-snippets-background-color: #FFF;
--newtab-snippets-hairline-color: transparent;
--trailhead-header-text-color: #2B2156;
--trailhead-cards-background-color: #EDEDF0;
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.1);
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.2);
--trailhead-card-button-background-active-color: rgba(12, 12, 13, 0.3); }
--newtab-snippets-hairline-color: transparent; }
body[lwt-newtab-brighttext] {
--newtab-background-color: #2A2A2E;
--newtab-border-primary-color: rgba(249, 249, 250, 0.8);
@ -140,12 +135,7 @@ body {
--newtab-card-placeholder-color: #4A4A4F;
--newtab-card-shadow: 0 1px 8px 0 rgba(12, 12, 13, 0.2);
--newtab-snippets-background-color: #38383D;
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1);
--trailhead-header-text-color: rgba(255, 255, 255, 0.6);
--trailhead-cards-background-color: rgba(12, 12, 13, 0.1);
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.3);
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.5);
--trailhead-card-button-background-active-color: rgba(12, 12, 13, 0.7); }
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1); }
.icon {
background-position: center center;
@ -3584,9 +3574,6 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.modalOverlayOuter.active {
display: flex; }
.modal-height {
padding-top: 80px; }
.modalOverlayInner {
min-width: min-content;
width: 100%;
@ -3607,17 +3594,6 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
border-radius: 0; } }
.modalOverlayInner.active {
display: block; }
.modalOverlayInner .icon-dismiss {
border: 0;
cursor: pointer;
inset-inline-end: 0;
padding: 20px;
fill: #FFF;
position: absolute; }
.modalOverlayInner .icon-dismiss:hover {
background-color: #2B2156; }
.modalOverlayInner .icon-dismiss:focus {
border: 1px dotted; }
.modalOverlayInner h2 {
color: #4A4A4F;
text-align: center;
@ -3990,305 +3966,6 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.submissionStatus .submitStatusTitle {
font-size: 20px; }
.onboardingMessageImage.addons {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-addons@2x.png"); }
.onboardingMessageImage.privatebrowsing {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-privatebrowsing@2x.png"); }
.onboardingMessageImage.screenshots {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-screenshots@2x.png"); }
.onboardingMessageImage.gift {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-gift@2x.png"); }
.onboardingMessageImage.sync {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-sync@2x.png"); }
.onboardingMessageImage.devices {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-devices.svg"); }
.onboardingMessageImage.fbcont {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-fbcont.svg"); }
.onboardingMessageImage.import {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-import.svg"); }
.onboardingMessageImage.ffmonitor {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffmonitor.svg"); }
.onboardingMessageImage.ffsend {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffsend.svg"); }
.onboardingMessageImage.lockwise {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-lockwise.svg"); }
.onboardingMessageImage.mobile {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-mobile.svg"); }
.onboardingMessageImage.pledge {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pledge.svg"); }
.onboardingMessageImage.pocket {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pocket.svg"); }
.onboardingMessageImage.private {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-private.svg"); }
.onboardingMessageImage.sendtab {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-sendtab.svg"); }
.onboardingMessageImage.tracking {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-tracking.svg"); }
.onboardingMessage {
height: 340px;
text-align: center;
padding: 13px;
font-weight: 200; }
@media (max-width: 850px) {
.onboardingMessage {
height: 170px;
text-align: left;
padding: 10px;
border-bottom: 1px solid #D7D7DB;
display: flex;
margin-bottom: 11px; }
.onboardingMessage:last-child {
border: 0; }
.onboardingMessage .onboardingContent {
padding-left: 10px;
height: 100%; }
.onboardingMessage .onboardingContent > span > h3 {
margin-top: 0;
margin-bottom: 4px;
font-weight: 400; }
.onboardingMessage .onboardingContent > span > p {
margin-top: 0;
line-height: 22px;
font-size: 15px; } }
@media (max-width: 650px) {
.onboardingMessage {
height: 250px; } }
.onboardingMessage .onboardingContent {
height: 175px; }
.onboardingMessage .onboardingContent > span > h3 {
color: #0C0C0D;
margin-bottom: 8px;
font-weight: 400; }
.onboardingMessage .onboardingContent > span > p {
color: #4A4A4F;
margin-top: 0;
height: 180px;
margin-bottom: 12px;
font-size: 15px;
line-height: 22px; }
@media (max-width: 650px) {
.onboardingMessage .onboardingContent > span > p {
margin-bottom: 0;
height: 160px; } }
.onboardingMessage .onboardingButton {
background-color: rgba(12, 12, 13, 0.1);
border: 0;
width: 150px;
height: 30px;
margin-bottom: 23px;
padding: 4px 0 6px;
font-size: 15px; }
@media (max-width: 850px) {
.onboardingMessage .onboardingButton {
float: right;
margin-top: -105px;
margin-inline-end: -10px; } }
@media (max-width: 650px) {
.onboardingMessage .onboardingButton {
float: none; } }
.onboardingMessage .onboardingButton:focus, .onboardingMessage .onboardingButton.active, .onboardingMessage .onboardingButton:hover {
box-shadow: 0 0 0 5px #D7D7DB;
transition: box-shadow 150ms; }
.onboardingMessage::before {
content: '';
height: 230px;
width: 1px;
position: absolute;
background-color: #D7D7DB;
margin-top: 40px;
margin-inline-start: 215px; }
@media (max-width: 850px) {
.onboardingMessage::before {
content: none; } }
.onboardingMessage:last-child::before {
content: none; }
.onboardingMessageImage {
height: 112px;
width: 180px;
background-size: auto 140px;
background-position: center center;
background-repeat: no-repeat;
display: inline-block; }
@media (max-width: 865px) {
.onboardingMessageImage {
height: 75px;
min-width: 80px;
background-size: 140px; } }
.trailheadCards {
background: var(--trailhead-cards-background-color);
overflow: hidden;
text-align: center;
transition: max-height 0.5s cubic-bezier(0.07, 0.95, 0, 1); }
@media (min-width: 610px) {
.trailheadCards {
max-height: 1000px; } }
.trailheadCards.collapsed {
max-height: 0; }
.trailheadCards h1 {
font-size: 36px;
font-weight: 200;
margin: 0 0 40px;
color: var(--trailhead-header-text-color); }
.trailheadCardsInner {
margin: auto;
padding: 40px 25px; }
@media (min-width: 610px) {
.trailheadCardsInner {
width: 530px; } }
@media (min-width: 866px) {
.trailheadCardsInner {
width: 786px; } }
@media (min-width: 1122px) {
.trailheadCardsInner {
width: 1042px; } }
.trailheadCardsInner .icon-dismiss {
border: 0;
border-radius: 4px;
cursor: pointer;
inset-inline-end: 15px;
padding: 15px;
opacity: 0.75;
position: absolute;
top: 15px; }
.trailheadCardsInner .icon-dismiss:hover, .trailheadCardsInner .icon-dismiss:focus {
background-color: var(--newtab-element-active-color); }
.trailheadCardGrid {
display: grid;
grid-gap: 32px;
margin: 0;
opacity: 0;
transition: opacity 0.4s;
transition-delay: 0.1s;
grid-auto-rows: 1fr; }
.trailheadCardGrid.show {
opacity: 1; }
@media (min-width: 610px) {
.trailheadCardGrid {
grid-template-columns: repeat(auto-fit, 224px); } }
@media (min-width: 1122px) {
.trailheadCardGrid {
grid-template-columns: repeat(auto-fit, 309px); } }
.trailheadCard {
position: relative;
background: var(--newtab-card-background-color);
border-radius: 4px;
box-shadow: var(--newtab-card-shadow);
font-size: 13px;
padding: 20px 20px 60px; }
@media (max-width: 865px) {
.trailheadCard {
padding: 20px; } }
@media (min-width: 1122px) {
.trailheadCard {
font-size: 15px; } }
.trailheadCard .onboardingTitle {
font-weight: normal;
color: var(--newtab-text-primary-color);
margin: 10px 0 4px;
font-size: 15px; }
@media (min-width: 1122px) {
.trailheadCard .onboardingTitle {
font-size: 18px; } }
.trailheadCard .onboardingText {
margin: 0 0 60px;
color: var(--newtab-text-conditional-color);
line-height: 1.5;
font-weight: 200; }
.trailheadCard .onboardingButton {
color: var(--newtab-text-conditional-color);
background: var(--trailhead-card-button-background-color);
border: 0;
margin: 14px;
min-width: 70%;
padding: 6px 14px;
white-space: pre-wrap; }
.trailheadCard .onboardingButton:focus, .trailheadCard .onboardingButton:hover {
box-shadow: none;
background: var(--trailhead-card-button-background-hover-color); }
.trailheadCard .onboardingButton:focus {
outline: dotted 1px; }
.trailheadCard .onboardingButton:active {
background: var(--trailhead-card-button-background-active-color); }
.trailheadCard .onboardingButtonContainer {
position: absolute;
bottom: 16px;
left: 0;
width: 100%;
text-align: center; }
.activity-stream.welcome {
overflow: hidden; }
.inline-onboarding.activity-stream.welcome {
overflow-y: hidden; }
.inline-onboarding .outer-wrapper {
position: relative;
display: block; }
.inline-onboarding .outer-wrapper .prefs-button button {
position: absolute; }
.inline-onboarding .asrouter-toggle {
position: absolute; }
.error {
display: none; }
.error.active {
display: block;
padding: 5px 12px;
animation: fade-down 450ms;
font-size: 12px;
font-weight: 500;
color: #FFF;
background-color: #D70022;
position: absolute;
inset-inline-start: 50px;
top: -28px;
border-radius: 2px; }
.error.active::before {
inset-inline-start: 12px;
background: #D70022;
bottom: -8px;
content: '.';
height: 16px;
position: absolute;
text-indent: -999px;
transform: rotate(45deg);
white-space: nowrap;
width: 16px;
z-index: -1; }
@keyframes fade-down {
0% {
opacity: 0;
transform: translateY(-15px); }
100% {
opacity: 1;
transform: translateY(0); } }
.EOYSnippetForm {
margin: 10px 0 8px;
align-self: start;

View File

@ -79,12 +79,7 @@ body {
--newtab-card-placeholder-color: #D7D7DB;
--newtab-card-shadow: 0 1px 4px 0 rgba(12, 12, 13, 0.1);
--newtab-snippets-background-color: #FFF;
--newtab-snippets-hairline-color: transparent;
--trailhead-header-text-color: #2B2156;
--trailhead-cards-background-color: #EDEDF0;
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.1);
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.2);
--trailhead-card-button-background-active-color: rgba(12, 12, 13, 0.3); }
--newtab-snippets-hairline-color: transparent; }
body[lwt-newtab-brighttext] {
--newtab-background-color: #2A2A2E;
--newtab-border-primary-color: rgba(249, 249, 250, 0.8);
@ -137,12 +132,7 @@ body {
--newtab-card-placeholder-color: #4A4A4F;
--newtab-card-shadow: 0 1px 8px 0 rgba(12, 12, 13, 0.2);
--newtab-snippets-background-color: #38383D;
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1);
--trailhead-header-text-color: rgba(255, 255, 255, 0.6);
--trailhead-cards-background-color: rgba(12, 12, 13, 0.1);
--trailhead-card-button-background-color: rgba(12, 12, 13, 0.3);
--trailhead-card-button-background-hover-color: rgba(12, 12, 13, 0.5);
--trailhead-card-button-background-active-color: rgba(12, 12, 13, 0.7); }
--newtab-snippets-hairline-color: rgba(255, 255, 255, 0.1); }
.icon {
background-position: center center;
@ -3581,9 +3571,6 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.modalOverlayOuter.active {
display: flex; }
.modal-height {
padding-top: 80px; }
.modalOverlayInner {
min-width: min-content;
width: 100%;
@ -3604,17 +3591,6 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
border-radius: 0; } }
.modalOverlayInner.active {
display: block; }
.modalOverlayInner .icon-dismiss {
border: 0;
cursor: pointer;
inset-inline-end: 0;
padding: 20px;
fill: #FFF;
position: absolute; }
.modalOverlayInner .icon-dismiss:hover {
background-color: #2B2156; }
.modalOverlayInner .icon-dismiss:focus {
border: 1px dotted; }
.modalOverlayInner h2 {
color: #4A4A4F;
text-align: center;
@ -3987,305 +3963,6 @@ body[lwt-newtab-brighttext] .scene2Icon .icon-light-theme {
.submissionStatus .submitStatusTitle {
font-size: 20px; }
.onboardingMessageImage.addons {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-addons@2x.png"); }
.onboardingMessageImage.privatebrowsing {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-privatebrowsing@2x.png"); }
.onboardingMessageImage.screenshots {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-screenshots@2x.png"); }
.onboardingMessageImage.gift {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-gift@2x.png"); }
.onboardingMessageImage.sync {
background-image: url("chrome://activity-stream/content/data/content/assets/illustration-sync@2x.png"); }
.onboardingMessageImage.devices {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-devices.svg"); }
.onboardingMessageImage.fbcont {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-fbcont.svg"); }
.onboardingMessageImage.import {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-import.svg"); }
.onboardingMessageImage.ffmonitor {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffmonitor.svg"); }
.onboardingMessageImage.ffsend {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-ffsend.svg"); }
.onboardingMessageImage.lockwise {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-lockwise.svg"); }
.onboardingMessageImage.mobile {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-mobile.svg"); }
.onboardingMessageImage.pledge {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pledge.svg"); }
.onboardingMessageImage.pocket {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-pocket.svg"); }
.onboardingMessageImage.private {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-private.svg"); }
.onboardingMessageImage.sendtab {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-sendtab.svg"); }
.onboardingMessageImage.tracking {
background-image: url("chrome://activity-stream/content/data/content/assets/trailhead/card-illo-tracking.svg"); }
.onboardingMessage {
height: 340px;
text-align: center;
padding: 13px;
font-weight: 200; }
@media (max-width: 850px) {
.onboardingMessage {
height: 170px;
text-align: left;
padding: 10px;
border-bottom: 1px solid #D7D7DB;
display: flex;
margin-bottom: 11px; }
.onboardingMessage:last-child {
border: 0; }
.onboardingMessage .onboardingContent {
padding-left: 10px;
height: 100%; }
.onboardingMessage .onboardingContent > span > h3 {
margin-top: 0;
margin-bottom: 4px;
font-weight: 400; }
.onboardingMessage .onboardingContent > span > p {
margin-top: 0;
line-height: 22px;
font-size: 15px; } }
@media (max-width: 650px) {
.onboardingMessage {
height: 250px; } }
.onboardingMessage .onboardingContent {
height: 175px; }
.onboardingMessage .onboardingContent > span > h3 {
color: #0C0C0D;
margin-bottom: 8px;
font-weight: 400; }
.onboardingMessage .onboardingContent > span > p {
color: #4A4A4F;
margin-top: 0;
height: 180px;
margin-bottom: 12px;
font-size: 15px;
line-height: 22px; }
@media (max-width: 650px) {
.onboardingMessage .onboardingContent > span > p {
margin-bottom: 0;
height: 160px; } }
.onboardingMessage .onboardingButton {
background-color: rgba(12, 12, 13, 0.1);
border: 0;
width: 150px;
height: 30px;
margin-bottom: 23px;
padding: 4px 0 6px;
font-size: 15px; }
@media (max-width: 850px) {
.onboardingMessage .onboardingButton {
float: right;
margin-top: -105px;
margin-inline-end: -10px; } }
@media (max-width: 650px) {
.onboardingMessage .onboardingButton {
float: none; } }
.onboardingMessage .onboardingButton:focus, .onboardingMessage .onboardingButton.active, .onboardingMessage .onboardingButton:hover {
box-shadow: 0 0 0 5px #D7D7DB;
transition: box-shadow 150ms; }
.onboardingMessage::before {
content: '';
height: 230px;
width: 1px;
position: absolute;
background-color: #D7D7DB;
margin-top: 40px;
margin-inline-start: 215px; }
@media (max-width: 850px) {
.onboardingMessage::before {
content: none; } }
.onboardingMessage:last-child::before {
content: none; }
.onboardingMessageImage {
height: 112px;
width: 180px;
background-size: auto 140px;
background-position: center center;
background-repeat: no-repeat;
display: inline-block; }
@media (max-width: 865px) {
.onboardingMessageImage {
height: 75px;
min-width: 80px;
background-size: 140px; } }
.trailheadCards {
background: var(--trailhead-cards-background-color);
overflow: hidden;
text-align: center;
transition: max-height 0.5s cubic-bezier(0.07, 0.95, 0, 1); }
@media (min-width: 610px) {
.trailheadCards {
max-height: 1000px; } }
.trailheadCards.collapsed {
max-height: 0; }
.trailheadCards h1 {
font-size: 36px;
font-weight: 200;
margin: 0 0 40px;
color: var(--trailhead-header-text-color); }
.trailheadCardsInner {
margin: auto;
padding: 40px 25px; }
@media (min-width: 610px) {
.trailheadCardsInner {
width: 530px; } }
@media (min-width: 866px) {
.trailheadCardsInner {
width: 786px; } }
@media (min-width: 1122px) {
.trailheadCardsInner {
width: 1042px; } }
.trailheadCardsInner .icon-dismiss {
border: 0;
border-radius: 4px;
cursor: pointer;
inset-inline-end: 15px;
padding: 15px;
opacity: 0.75;
position: absolute;
top: 15px; }
.trailheadCardsInner .icon-dismiss:hover, .trailheadCardsInner .icon-dismiss:focus {
background-color: var(--newtab-element-active-color); }
.trailheadCardGrid {
display: grid;
grid-gap: 32px;
margin: 0;
opacity: 0;
transition: opacity 0.4s;
transition-delay: 0.1s;
grid-auto-rows: 1fr; }
.trailheadCardGrid.show {
opacity: 1; }
@media (min-width: 610px) {
.trailheadCardGrid {
grid-template-columns: repeat(auto-fit, 224px); } }
@media (min-width: 1122px) {
.trailheadCardGrid {
grid-template-columns: repeat(auto-fit, 309px); } }
.trailheadCard {
position: relative;
background: var(--newtab-card-background-color);
border-radius: 4px;
box-shadow: var(--newtab-card-shadow);
font-size: 13px;
padding: 20px 20px 60px; }
@media (max-width: 865px) {
.trailheadCard {
padding: 20px; } }
@media (min-width: 1122px) {
.trailheadCard {
font-size: 15px; } }
.trailheadCard .onboardingTitle {
font-weight: normal;
color: var(--newtab-text-primary-color);
margin: 10px 0 4px;
font-size: 15px; }
@media (min-width: 1122px) {
.trailheadCard .onboardingTitle {
font-size: 18px; } }
.trailheadCard .onboardingText {
margin: 0 0 60px;
color: var(--newtab-text-conditional-color);
line-height: 1.5;
font-weight: 200; }
.trailheadCard .onboardingButton {
color: var(--newtab-text-conditional-color);
background: var(--trailhead-card-button-background-color);
border: 0;
margin: 14px;
min-width: 70%;
padding: 6px 14px;
white-space: pre-wrap; }
.trailheadCard .onboardingButton:focus, .trailheadCard .onboardingButton:hover {
box-shadow: none;
background: var(--trailhead-card-button-background-hover-color); }
.trailheadCard .onboardingButton:focus {
outline: dotted 1px; }
.trailheadCard .onboardingButton:active {
background: var(--trailhead-card-button-background-active-color); }
.trailheadCard .onboardingButtonContainer {
position: absolute;
bottom: 16px;
left: 0;
width: 100%;
text-align: center; }
.activity-stream.welcome {
overflow: hidden; }
.inline-onboarding.activity-stream.welcome {
overflow-y: hidden; }
.inline-onboarding .outer-wrapper {
position: relative;
display: block; }
.inline-onboarding .outer-wrapper .prefs-button button {
position: absolute; }
.inline-onboarding .asrouter-toggle {
position: absolute; }
.error {
display: none; }
.error.active {
display: block;
padding: 5px 12px;
animation: fade-down 450ms;
font-size: 12px;
font-weight: 500;
color: #FFF;
background-color: #D70022;
position: absolute;
inset-inline-start: 50px;
top: -28px;
border-radius: 2px; }
.error.active::before {
inset-inline-start: 12px;
background: #D70022;
bottom: -8px;
content: '.';
height: 16px;
position: absolute;
text-indent: -999px;
transform: rotate(45deg);
white-space: nowrap;
width: 16px;
z-index: -1; }
@keyframes fade-down {
0% {
opacity: 0;
transform: translateY(-15px); }
100% {
opacity: 1;
transform: translateY(0); } }
.EOYSnippetForm {
margin: 10px 0 8px;
align-self: start;

View File

@ -37,12 +37,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
XPCOMUtils.defineLazyServiceGetters(this, {
BrowserHandler: ["@mozilla.org/browser/clh;1", "nsIBrowserHandler"],
});
XPCOMUtils.defineLazyPreferenceGetter(
this,
"isSeparateAboutWelcome",
"browser.aboutwelcome.enabled",
true
);
const { actionCreators: ac } = ChromeUtils.import(
"resource://activity-stream/common/Actions.jsm"
);
@ -63,11 +57,6 @@ const { AttributionCode } = ChromeUtils.import(
"resource:///modules/AttributionCode.jsm"
);
const TRAILHEAD_CONFIG = {
DID_SEE_ABOUT_WELCOME_PREF: "trailhead.firstrun.didSeeAboutWelcome",
DYNAMIC_TRIPLET_BUNDLE_LENGTH: 3,
};
// List of hosts for endpoints that serve router messages.
// Key is allowed host, value is a name for the endpoint host.
const DEFAULT_ALLOWLIST_HOSTS = {
@ -1021,7 +1010,6 @@ class _ASRouter {
providerPrefs: ASRouterPreferences.providers,
userPrefs: ASRouterPreferences.getAllUserPreferences(),
targetingParameters,
trailheadTriplet: ASRouterPreferences.trailheadTriplet,
errors: this.errors,
}));
}
@ -1097,10 +1085,6 @@ class _ASRouter {
return Promise.resolve({ evaluationStatus });
}
_orderBundle(bundle) {
return bundle.sort((a, b) => a.order - b.order);
}
unblockAll() {
return this.setState({ messageBlockList: [] });
}
@ -1160,97 +1144,6 @@ class _ASRouter {
return true;
}
async _getBundledMessages(originalMessage, trigger, force = false) {
let result = [];
let bundleLength;
let bundleTemplate;
let originalId;
if (originalMessage.includeBundle) {
// The original message is not part of the bundle, so don't include it
bundleLength = originalMessage.includeBundle.length;
bundleTemplate = originalMessage.includeBundle.template;
} else {
// The original message is part of the bundle
bundleLength = originalMessage.bundled;
bundleTemplate = originalMessage.template;
originalId = originalMessage.id;
// Add in a copy of the first message
result.push({
content: originalMessage.content,
id: originalMessage.id,
order: originalMessage.order || 0,
});
}
// First, find all messages of same template. These are potential matching targeting candidates
let bundledMessagesOfSameTemplate = this.state.messages.filter(
msg =>
msg.bundled &&
msg.template === bundleTemplate &&
msg.id !== originalId &&
this.isUnblockedMessage(msg)
);
if (force) {
// Forcefully show the messages without targeting matching - this is for about:newtab#asrouter to show the messages
for (const message of bundledMessagesOfSameTemplate) {
result.push({ content: message.content, id: message.id });
// Stop once we have enough messages to fill a bundle
if (result.length === bundleLength) {
break;
}
}
} else {
// Find all messages that matches the targeting context
const allMessages = await this.handleMessageRequest({
messages: bundledMessagesOfSameTemplate,
triggerId: trigger && trigger.id,
triggerContext: trigger && trigger.context,
triggerParam: trigger && trigger.param,
ordered: true,
returnAll: true,
});
if (allMessages && allMessages.length) {
// Retrieve enough messages needed to fill a bundle
// Only copy the content of the message (that's what the UI cares about)
result = result.concat(
allMessages.slice(0, bundleLength).map(message => ({
content: message.content,
id: message.id,
order: message.order || 0,
// This is used to determine whether to block when action is triggered
// Only block for dynamic triplets experiment and when there are more messages available
blockOnClick:
ASRouterPreferences.trailheadTriplet.startsWith("dynamic") &&
allMessages.length >
TRAILHEAD_CONFIG.DYNAMIC_TRIPLET_BUNDLE_LENGTH,
}))
);
}
}
// If we did not find enough messages to fill the bundle, do not send the bundle down
if (result.length < bundleLength) {
return null;
}
// The bundle may have some extra attributes, like a header, or a dismiss button, so attempt to get those strings now
// This is a temporary solution until we can use Fluent strings in the content process, in which case the content can
// handle finding these strings on its own. See bug 1488973
const extraTemplateStrings = await this._extraTemplateStrings(
originalMessage
);
return {
bundle: this._orderBundle(result),
...(extraTemplateStrings && { extraTemplateStrings }),
provider: originalMessage.provider,
template: originalMessage.template,
};
}
async _extraTemplateStrings(originalMessage) {
let extraTemplateStrings;
let localProvider = this._findProvider(originalMessage.provider);
@ -1268,6 +1161,10 @@ class _ASRouter {
}
routeCFRMessage(message, browser, trigger, force = false) {
if (!message) {
return { message: {} };
}
switch (message.template) {
case "whatsnew_panel_message":
if (force) {
@ -1318,33 +1215,7 @@ class _ASRouter {
case "update_action":
MomentsPageHub.executeAction(message);
break;
default:
break;
}
}
async sendMessage(message, trigger, force, browser) {
if (!message) {
return { message: {} };
} else if (message.bundled) {
const bundle =
(await this._getBundledMessages(message, trigger, force)) || {};
return { message: bundle };
} else if (message.includeBundle) {
const bundledMessages = await this._getBundledMessages(
message,
message.includeBundle.trigger,
force
);
return {
message: {
...message,
trailheadTriplet: ASRouterPreferences.trailheadTriplet || "",
bundle: bundledMessages && bundledMessages.bundle,
},
};
}
this.routeCFRMessage(message, browser, trigger, force);
return { message };
}
@ -1537,7 +1408,7 @@ class _ASRouter {
}
setMessageById({ id, ...data }, force, browser) {
return this.sendMessage(this.getMessageById(id), data, force, browser);
return this.routeCFRMessage(this.getMessageById(id), browser, data, force);
}
blockMessageById(idOrIds) {
@ -1764,21 +1635,11 @@ class _ASRouter {
} else {
const telemetryObject = { tabId };
TelemetryStopwatch.start("MS_MESSAGE_REQUEST_TIME_MS", telemetryObject);
// On new tab, send cards if they match and not part of default multistage onboarding experience;
// othwerise send a snippet
if (!isSeparateAboutWelcome) {
message = await this.handleMessageRequest({
template: "extended_triplets",
});
}
// If no extended triplets message was returned, show snippets instead
if (!message) {
message = await this.handleMessageRequest({ provider: "snippets" });
}
message = await this.handleMessageRequest({ provider: "snippets" });
TelemetryStopwatch.finish("MS_MESSAGE_REQUEST_TIME_MS", telemetryObject);
}
return this.sendMessage(message, undefined, false, browser);
return this.routeCFRMessage(message, browser, undefined, false);
}
_recordReachEvent(message) {
@ -1835,11 +1696,11 @@ class _ASRouter {
);
}
return this.sendMessage(
return this.routeCFRMessage(
nonReachMessages[0] || null,
browser,
trigger,
false,
browser
false
);
}
@ -1867,7 +1728,6 @@ class _ASRouter {
}
}
this._ASRouter = _ASRouter;
this.TRAILHEAD_CONFIG = TRAILHEAD_CONFIG;
/**
* ASRouter - singleton instance of _ASRouter that controls all messages
@ -1875,9 +1735,4 @@ this.TRAILHEAD_CONFIG = TRAILHEAD_CONFIG;
*/
this.ASRouter = new _ASRouter();
const EXPORTED_SYMBOLS = [
"_ASRouter",
"ASRouter",
"MessageLoaderUtils",
"TRAILHEAD_CONFIG",
];
const EXPORTED_SYMBOLS = ["_ASRouter", "ASRouter", "MessageLoaderUtils"];

View File

@ -13,14 +13,6 @@ const PROVIDER_PREF_BRANCH =
const DEVTOOLS_PREF =
"browser.newtabpage.activity-stream.asrouter.devtoolsEnabled";
const FXA_USERNAME_PREF = "services.sync.username";
const FIRST_RUN_TRIPLET_PREF = "trailhead.firstrun.newtab.triplets";
XPCOMUtils.defineLazyPreferenceGetter(
this,
"trailheadTripletPref",
FIRST_RUN_TRIPLET_PREF,
""
);
const DEFAULT_STATE = {
_initialized: false,
@ -106,11 +98,6 @@ class _ASRouterPreferences {
}, []);
}
// istanbul ignore next
get trailheadTriplet() {
return trailheadTripletPref;
}
get providers() {
if (!this._initialized || this._providers === null) {
const config = this._getProviderConfig();

View File

@ -420,9 +420,6 @@ const TargetingGetters = {
get isFxAEnabled() {
return isFxAEnabled;
},
get trailheadTriplet() {
return ASRouterPreferences.trailheadTriplet;
},
get sync() {
return {
desktopDevices: clientsDevicesDesktop,

View File

@ -3,9 +3,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/* globals Localization */
const { FX_MONITOR_OAUTH_CLIENT_ID } = ChromeUtils.import(
"resource://gre/modules/FxAccountsCommon.js"
);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const L10N = new Localization([
@ -16,289 +13,6 @@ const L10N = new Localization([
]);
const ONBOARDING_MESSAGES = () => [
{
id: "EXTENDED_TRIPLETS_1",
template: "extended_triplets",
campaign: "firstrun_triplets",
targeting:
"trailheadTriplet && ((currentDate|date - profileAgeCreated) / 86400000) < 7",
includeBundle: {
length: 3,
template: "onboarding",
trigger: { id: "showOnboarding" },
},
frequency: { lifetime: 5 },
utm_term: "trailhead-cards",
},
{
id: "TRAILHEAD_CARD_1",
template: "onboarding",
bundled: 3,
order: 3,
content: {
title: { string_id: "onboarding-tracking-protection-title2" },
text: { string_id: "onboarding-tracking-protection-text2" },
icon: "tracking",
primary_button: {
label: { string_id: "onboarding-tracking-protection-button2" },
action:
Services.locale.appLocaleAsBCP47.substr(0, 2) === "en"
? {
type: "OPEN_URL",
data: {
args: "https://mzl.la/ETPdefault",
where: "tabshifted",
},
}
: {
type: "OPEN_PREFERENCES_PAGE",
data: { category: "privacy-trackingprotection" },
},
},
},
targeting: "trailheadTriplet == 'privacy'",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_2",
template: "onboarding",
bundled: 3,
order: 1,
content: {
title: { string_id: "onboarding-data-sync-title" },
text: { string_id: "onboarding-data-sync-text2" },
icon: "devices",
primary_button: {
label: { string_id: "onboarding-data-sync-button2" },
action: {
type: "OPEN_URL",
addFlowParams: true,
data: {
args:
"https://accounts.firefox.com/?service=sync&action=email&context=fx_desktop_v3&entrypoint=activity-stream-firstrun&style=trailhead",
where: "tabshifted",
},
},
},
},
targeting:
"(trailheadTriplet in ['supercharge', 'static'] || ( 'dynamic' in trailheadTriplet && usesFirefoxSync == false)) && isChinaRepack == false",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_3",
template: "onboarding",
bundled: 3,
order: 2,
content: {
title: { string_id: "onboarding-firefox-monitor-title" },
text: { string_id: "onboarding-firefox-monitor-text2" },
icon: "ffmonitor",
primary_button: {
label: { string_id: "onboarding-firefox-monitor-button" },
action: {
type: "OPEN_URL",
data: { args: "https://monitor.firefox.com/", where: "tabshifted" },
},
},
},
// Use service oauth client_id to identify 'Firefox Monitor' service attached to Firefox Account
// https://docs.telemetry.mozilla.org/datasets/fxa_metrics/attribution.html#service-attribution
targeting: `(trailheadTriplet in ['supercharge', 'static', 'privacy'] || ('dynamic' in trailheadTriplet && !("${FX_MONITOR_OAUTH_CLIENT_ID}" in attachedFxAOAuthClients|mapToProperty('id')))) && isChinaRepack == false`,
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_4",
template: "onboarding",
bundled: 3,
order: 3,
content: {
title: { string_id: "onboarding-browse-privately-title" },
text: { string_id: "onboarding-browse-privately-text" },
icon: "private",
primary_button: {
label: { string_id: "onboarding-browse-privately-button" },
action: { type: "OPEN_PRIVATE_BROWSER_WINDOW" },
},
},
targeting: "'dynamic' in trailheadTriplet",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_5",
template: "onboarding",
bundled: 3,
order: 5,
content: {
title: { string_id: "onboarding-firefox-send-title" },
text: { string_id: "onboarding-firefox-send-text2" },
icon: "ffsend",
primary_button: {
label: { string_id: "onboarding-firefox-send-button" },
action: {
type: "OPEN_URL",
data: { args: "https://send.firefox.com/", where: "tabshifted" },
},
},
},
targeting: "trailheadTriplet == 'payoff' && isChinaRepack == false",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_6",
template: "onboarding",
bundled: 3,
order: 6,
content: {
title: { string_id: "onboarding-mobile-phone-title" },
text: { string_id: "onboarding-mobile-phone-text" },
icon: "mobile",
primary_button: {
label: { string_id: "onboarding-mobile-phone-button" },
action: {
type: "OPEN_URL",
data: {
args: "https://www.mozilla.org/firefox/mobile/",
where: "tabshifted",
},
},
},
},
targeting:
"trailheadTriplet in ['supercharge', 'static'] || ('dynamic' in trailheadTriplet && sync.mobileDevices < 1)",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_7",
template: "onboarding",
bundled: 3,
order: 4,
content: {
title: { string_id: "onboarding-send-tabs-title" },
text: { string_id: "onboarding-send-tabs-text2" },
icon: "sendtab",
primary_button: {
label: { string_id: "onboarding-send-tabs-button" },
action: {
type: "OPEN_URL",
data: {
args:
"https://support.mozilla.org/kb/send-tab-firefox-desktop-other-devices",
where: "tabshifted",
},
},
},
},
targeting: "'dynamic' in trailheadTriplet",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_8",
template: "onboarding",
bundled: 3,
order: 2,
content: {
title: { string_id: "onboarding-pocket-anywhere-title" },
text: { string_id: "onboarding-pocket-anywhere-text2" },
icon: "pocket",
primary_button: {
label: { string_id: "onboarding-pocket-anywhere-button" },
action: {
type: "OPEN_URL",
data: {
args: "https://getpocket.com/firefox_learnmore",
where: "tabshifted",
},
},
},
},
targeting: "trailheadTriplet == 'multidevice' && isChinaRepack == false",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_9",
template: "onboarding",
bundled: 3,
order: 7,
content: {
title: { string_id: "onboarding-lockwise-strong-passwords-title" },
text: { string_id: "onboarding-lockwise-strong-passwords-text" },
icon: "lockwise",
primary_button: {
label: { string_id: "onboarding-lockwise-strong-passwords-button" },
action: {
type: "OPEN_ABOUT_PAGE",
data: { args: "logins", where: "tabshifted" },
},
},
},
targeting: "'dynamic' in trailheadTriplet && isChinaRepack == false",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_10",
template: "onboarding",
bundled: 3,
order: 4,
content: {
title: { string_id: "onboarding-facebook-container-title" },
text: { string_id: "onboarding-facebook-container-text2" },
icon: "fbcont",
primary_button: {
label: { string_id: "onboarding-facebook-container-button" },
action: {
type: "OPEN_URL",
data: {
args:
"https://addons.mozilla.org/firefox/addon/facebook-container/",
where: "tabshifted",
},
},
},
},
targeting: "trailheadTriplet == 'payoff' && isChinaRepack == false",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_11",
template: "onboarding",
bundled: 3,
order: 0,
content: {
title: { string_id: "onboarding-import-browser-settings-title" },
text: { string_id: "onboarding-import-browser-settings-text" },
icon: "import",
primary_button: {
label: { string_id: "onboarding-import-browser-settings-button" },
action: { type: "SHOW_MIGRATION_WIZARD" },
},
},
targeting: "trailheadTriplet == 'dynamic_chrome'",
trigger: { id: "showOnboarding" },
},
{
id: "TRAILHEAD_CARD_12",
template: "onboarding",
bundled: 3,
order: 1,
content: {
title: { string_id: "onboarding-personal-data-promise-title" },
text: { string_id: "onboarding-personal-data-promise-text" },
icon: "pledge",
primary_button: {
label: { string_id: "onboarding-personal-data-promise-button" },
action: {
type: "OPEN_URL",
data: {
args: "https://www.mozilla.org/firefox/privacy/",
where: "tabshifted",
},
},
},
},
targeting: "trailheadTriplet == 'privacy'",
trigger: { id: "showOnboarding" },
},
{
id: "FXA_ACCOUNTS_BADGE",
template: "toolbar_badge",

View File

@ -16,7 +16,6 @@
<link rel="stylesheet" href="chrome://activity-stream/content/css/activity-stream.css" />
</head>
<body class="activity-stream">
<div id="header-asrouter-container" role="presentation"></div>
<div id="root"></div>
<div id="footer-asrouter-container" role="presentation"></div>
<script src="chrome://browser/content/contentSearchUI.js"></script>

View File

@ -16,7 +16,6 @@
<link rel="stylesheet" href="chrome://activity-stream/content/css/activity-stream.css" />
</head>
<body class="activity-stream">
<div id="header-asrouter-container" role="presentation"></div>
<div id="root"></div>
<div id="footer-asrouter-container" role="presentation"></div>
</body>

View File

@ -16,7 +16,6 @@
<link rel="stylesheet" href="chrome://activity-stream/content/css/activity-stream.css" />
</head>
<body class="activity-stream">
<div id="header-asrouter-container" role="presentation"></div>
<div id="root"></div>
<div id="footer-asrouter-container" role="presentation"></div>
<script src="chrome://browser/content/contentSearchUI.js"></script>

View File

@ -126,8 +126,6 @@ describe("ASRouter", () => {
profileAgeReset: {},
usesFirefoxSync: false,
isFxAEnabled: true,
trailheadInterrupt: "join",
trailheadTriplet: "dynamic",
sync: {
desktopDevices: 0,
mobileDevices: 0,
@ -186,9 +184,6 @@ describe("ASRouter", () => {
sandbox.spy(ASRouterPreferences, "uninit");
sandbox.spy(ASRouterPreferences, "addListener");
sandbox.spy(ASRouterPreferences, "removeListener");
sandbox.stub(ASRouterPreferences, "trailheadTriplet").get(() => {
return "test";
});
sandbox.replaceGetter(
ASRouterPreferences,
"personalizedCfrScores",
@ -651,7 +646,6 @@ describe("ASRouter", () => {
ASRouterPreferences.getAllUserPreferences()
);
assert.deepEqual(state.targetingParameters, {});
assert.deepEqual(state.trailhead, ASRouterPreferences.trailhead);
assert.deepEqual(state.errors, Router.errors);
});
it("should not send a message on a state change asrouter.devtoolsEnabled pref is on", async () => {
@ -1521,26 +1515,6 @@ describe("ASRouter", () => {
assert.deepEqual(response.message, message);
});
it("should send an empty object message if no bundle is available", async () => {
// force the only message to be a bundled message that needs 2 messages in the bundle
await Router.setState({
messages: [
{
id: "foo1",
groups: ["snippets"],
provider: "snippets",
template: "simple_template",
bundled: 2,
content: { title: "Foo1", body: "Foo123-1" },
},
],
});
let response = await Router.sendNewTabMessage({
tabId: 0,
browser: {},
});
assert.deepEqual(response.message, {});
});
it("should send an empty object message if no messages are available", async () => {
await Router.setState({ messages: [] });
let response = await Router.sendNewTabMessage({
@ -1609,89 +1583,6 @@ describe("ASRouter", () => {
});
});
it("should handle onboarding message provider", async () => {
const handleMessageRequestStub = sandbox.stub(
Router,
"handleMessageRequest"
);
handleMessageRequestStub
.withArgs({
template: "extended_triplets",
})
.resolves({ id: "foo" });
sandbox.stub(Router, "sendNewTabMessage").resolves();
await Router.sendNewTabMessage({
tabId: 0,
browser: {},
});
assert.calledOnce(Router.sendNewTabMessage);
});
it("should hide extended triplets by default when browser.aboutwelcome.enabled is true", async () => {
const handleMessageRequestStub = sandbox.stub(
Router,
"handleMessageRequest"
);
handleMessageRequestStub
.withArgs({
template: "extended_triplets",
})
.resolves({ id: "foo" });
await Router.sendNewTabMessage({
tabId: 0,
browser: {},
});
assert.calledOnce(handleMessageRequestStub);
assert.calledWithExactly(handleMessageRequestStub, {
provider: "snippets",
});
});
it("should show extended triplets when browser.aboutwelcome.enabled is false", async () => {
globals.set({ isSeparateAboutWelcome: false });
const handleMessageRequestStub = sandbox.stub(
Router,
"handleMessageRequest"
);
handleMessageRequestStub
.withArgs({
template: "extended_triplets",
})
.resolves({ id: "foo" });
await Router.sendNewTabMessage({
tabId: 0,
browser: {},
});
assert.calledOnce(handleMessageRequestStub);
assert.calledWithExactly(handleMessageRequestStub, {
template: "extended_triplets",
});
});
it("should fallback to snippets if onboarding message provider returned none when browser.aboutwelcome.enabled is false", async () => {
globals.set({ isSeparateAboutWelcome: false });
const handleMessageRequestStub = sandbox.stub(
Router,
"handleMessageRequest"
);
handleMessageRequestStub
.withArgs({
template: "extended_triplets",
})
.resolves(null);
await Router.sendNewTabMessage({
tabId: 0,
browser: {},
});
assert.calledTwice(handleMessageRequestStub);
assert.calledWithExactly(handleMessageRequestStub, {
template: "extended_triplets",
});
assert.calledWithExactly(handleMessageRequestStub, {
provider: "snippets",
});
});
it("should record telemetry for message request duration", async () => {
const startTelemetryStopwatch = sandbox.stub(
global.TelemetryStopwatch,
@ -1754,65 +1645,6 @@ describe("ASRouter", () => {
});
describe("#setMessageById", async () => {
it("should call _getBundledMessages if we request a message that needs to be bundled", async () => {
sandbox.stub(Router, "_getBundledMessages").resolves();
// forcefully pick a message which needs to be bundled (the second message in FAKE_LOCAL_MESSAGES)
const [, testMessage] = Router.state.messages;
await Router.setMessageById({ id: testMessage.id });
assert.calledOnce(Router._getBundledMessages);
});
it("should properly pick another message of the same template if it is bundled; force = true", async () => {
// forcefully pick a message which needs to be bundled (the second message in FAKE_LOCAL_MESSAGES)
const [, testMessage1, testMessage2] = Router.state.messages;
let response = await Router.setMessageById(
{ id: testMessage1.id },
true /* force */
);
// Expected object should have some properties of the original message it picked (testMessage1)
// plus the bundled content of the others that it picked of the same template (testMessage2)
const expectedObj = {
template: testMessage1.template,
provider: testMessage1.provider,
bundle: [
{ content: testMessage1.content, id: testMessage1.id, order: 1 },
{ content: testMessage2.content, id: testMessage2.id },
],
};
assert.deepEqual(response.message, expectedObj);
});
it("should properly pick another message of the same template if it is bundled; force = false", async () => {
// forcefully pick a message which needs to be bundled (the second message in FAKE_LOCAL_MESSAGES)
const [, testMessage1, testMessage2] = Router.state.messages;
ASRouterTargeting.findMatchingMessage.callsFake(({ messages }) => [
messages[0],
]);
let response = await Router.setMessageById(
{ id: testMessage1.id },
false
);
// Expected object should have some properties of the original message it picked (testMessage1)
// plus the bundled content of the others that it picked of the same template (testMessage2)
const expectedObj = {
template: testMessage1.template,
provider: testMessage1.provider,
bundle: [
{ content: testMessage1.content, id: testMessage1.id, order: 1 },
{
content: testMessage2.content,
id: testMessage2.id,
order: 2,
blockOnClick: false,
},
],
};
assert.deepEqual(response.message, expectedObj);
});
it("should send an empty message if provided id did not resolve to a message", async () => {
let response = await Router.setMessageById({ id: -1 }, true, {});
assert.deepEqual(response.message, {});
@ -1917,33 +1749,33 @@ describe("ASRouter", () => {
});
});
describe("#sendMessage", () => {
it("should allow for echoing back message modifications", async () => {
describe("#routeCFRMessage", () => {
it("should allow for echoing back message modifications", () => {
const message = { somekey: "some value" };
const data = { content: message };
const browser = {};
let msg = await Router.sendMessage(data.content, data, false, browser);
let msg = Router.routeCFRMessage(data.content, browser, data, false);
assert.deepEqual(msg.message, message);
});
it("should call CFRPageActions.forceRecommendation if the template is cfr_action and force is true", async () => {
sandbox.stub(CFRPageActions, "forceRecommendation");
const testMessage = { id: "foo", template: "cfr_doorhanger" };
await Router.setState({ messages: [testMessage] });
await Router.sendMessage(testMessage, null, true, {});
Router.routeCFRMessage(testMessage, {}, null, true);
assert.calledOnce(CFRPageActions.forceRecommendation);
});
it("should call BookmarkPanelHub.forceShowMessage the provider is cfr-fxa", async () => {
const testMessage = { id: "foo", template: "fxa_bookmark_panel" };
await Router.setState({ messages: [testMessage] });
await Router.sendMessage(testMessage, null, true, {});
Router.routeCFRMessage(testMessage, {}, null, true);
assert.calledOnce(FakeBookmarkPanelHub.forceShowMessage);
});
it("should call CFRPageActions.addRecommendation if the template is cfr_action and force is false", async () => {
sandbox.stub(CFRPageActions, "addRecommendation");
const testMessage = { id: "foo", template: "cfr_doorhanger" };
await Router.setState({ messages: [testMessage] });
await Router.sendMessage(testMessage, {}, false, {});
Router.routeCFRMessage(testMessage, {}, {}, false);
assert.calledOnce(CFRPageActions.addRecommendation);
});
});
@ -1956,7 +1788,6 @@ describe("ASRouter", () => {
providerPrefs: ASRouterPreferences.providers,
userPrefs: ASRouterPreferences.getAllUserPreferences(),
targetingParameters: {},
trailheadTriplet: ASRouterPreferences.trailheadTriplet,
errors: Router.errors,
});
@ -2132,48 +1963,6 @@ describe("ASRouter", () => {
});
});
describe("_getBundledMessages", () => {
it("should return a null bundle if we do not have enough messages to fill the bundle", async () => {
// force the only message to be a bundled message that needs 2 messages in the bundle
await Router.setState({
messages: [
{
id: "foo1",
template: "simple_template",
bundled: 2,
content: { title: "Foo1", body: "Foo123-1" },
groups: ["bundle"],
provider: "snippets",
},
],
providers: [{ id: "snippets" }],
});
const bundle = await Router._getBundledMessages(Router.state.messages[0]);
assert.equal(bundle, null);
});
it("should send down extra attributes in the bundle if they exist", async () => {
sandbox.stub(Router, "_findProvider").returns({
getExtraAttributes() {
return Promise.resolve({ header: "header" });
},
});
await Router.setState({
messages: [
{
id: "foo1",
template: "simple_template",
bundled: 1,
content: { title: "Foo1", body: "Foo123-1" },
groups: ["snippets"],
},
],
providers: [{ id: "snippets" }],
});
const result = await Router._getBundledMessages(Router.state.messages[0]);
assert.equal(result.extraTemplateStrings.header, "header");
});
});
describe("forceAttribution", () => {
let setReferrerUrl;
beforeEach(() => {

View File

@ -87,7 +87,7 @@ describe("#CachedTargetingGetter", () => {
const [
m1,
m2,
m3,
m3 = { id: "m3" },
] = await OnboardingMessageProvider.getUntranslatedMessages();
const checkMessageTargetingStub = sandbox
.stub(ASRouterTargeting, "checkMessageTargeting")
@ -118,7 +118,7 @@ describe("#CachedTargetingGetter", () => {
const [
m1,
m2,
m3,
m3 = { id: "m3" },
] = await OnboardingMessageProvider.getUntranslatedMessages();
const checkMessageTargetingStub = sandbox
.stub(ASRouterTargeting, "checkMessageTargeting")
@ -149,7 +149,7 @@ describe("#CachedTargetingGetter", () => {
const [
m1,
m2,
m3,
m3 = { id: "m3" },
] = await OnboardingMessageProvider.getUntranslatedMessages();
const checkMessageTargetingStub = sandbox
.stub(ASRouterTargeting, "checkMessageTargeting")

View File

@ -4,7 +4,7 @@ import docs from "content-src/asrouter/docs/targeting-attributes.md";
// The following targeting parameters are either deprecated or should not be included in the docs for some reason.
const SKIP_DOCS = [];
// These are extra message context attributes via ASRouter.jsm
const MESSAGE_CONTEXT_ATTRIBUTES = ["previousSessionEnd", "trailheadTriplet"];
const MESSAGE_CONTEXT_ATTRIBUTES = ["previousSessionEnd"];
function getHeadingsFromDocs() {
const re = /### `(\w+)`/g;

View File

@ -225,39 +225,6 @@ describe("ASRouterUISurface", () => {
});
});
describe("Triplet bundle Card", () => {
it("should send NEW_TAB_MESSAGE_REQUEST if a bundle card id is blocked or cleared", async () => {
sandbox.stub(ASRouterUtils, "sendMessage").resolves();
const FAKE_TRIPLETS_BUNDLE_1 = [
{
id: "CARD_1",
content: {
title: { string_id: "onboarding-private-browsing-title" },
text: { string_id: "onboarding-private-browsing-text" },
icon: "icon",
primary_button: {
label: { string_id: "onboarding-button-label-get-started" },
action: {
type: "OPEN_URL",
data: { args: "https://example.com/" },
},
},
},
},
];
wrapper.setState({
message: { bundle: FAKE_TRIPLETS_BUNDLE_1 },
});
wrapper.instance().clearMessage("CARD_1");
assert.calledOnce(ASRouterUtils.sendMessage);
assert.calledWithExactly(ASRouterUtils.sendMessage, {
type: "NEWTAB_MESSAGE_REQUEST",
data: { endpoint: undefined },
});
});
});
describe("impressions", () => {
function simulateVisibilityChange(value) {
fakeDocument.visibilityState = value;

View File

@ -1,225 +0,0 @@
import {
FirstRun,
FLUENT_FILES,
} from "content-src/asrouter/templates/FirstRun/FirstRun";
import { Triplets } from "content-src/asrouter/templates/FirstRun/Triplets";
import { OnboardingMessageProvider } from "lib/OnboardingMessageProvider.jsm";
import { mount } from "enzyme";
import React from "react";
const FAKE_TRIPLETS_BUNDLE_1 = [
{
id: "CARD_1",
content: {
title: { string_id: "onboarding-private-browsing-title" },
text: { string_id: "onboarding-private-browsing-text" },
icon: "icon",
primary_button: {
label: { string_id: "onboarding-button-label-get-started" },
action: {
type: "OPEN_URL",
data: { args: "https://example.com/" },
},
},
},
},
];
const FAKE_TRIPLETS_BUNDLE_2 = [
{
id: "CARD_2",
content: {
title: { string_id: "onboarding-data-sync-title" },
text: { string_id: "onboarding-data-sync-text2" },
icon: "icon",
primary_button: {
label: { string_id: "onboarding-data-sync-button2" },
action: {
type: "OPEN_URL",
data: { args: "https://foo.com/" },
},
},
},
},
];
const FAKE_FLOW_PARAMS = {
deviceId: "foo",
flowId: "abc1",
flowBeginTime: 1234,
};
async function getTestMessage(id, requestNewBundle) {
const message = (
await OnboardingMessageProvider.getUntranslatedMessages()
).find(msg => msg.id === id);
// Simulate dynamic triplets by returning a different bundle
if (requestNewBundle) {
return { ...message, bundle: FAKE_TRIPLETS_BUNDLE_2 };
}
return { ...message, bundle: FAKE_TRIPLETS_BUNDLE_1 };
}
describe("<FirstRun>", () => {
let wrapper;
let message;
let fakeDoc;
let sandbox;
let clock;
let onBlockByIdStub;
async function setup() {
sandbox = sinon.createSandbox();
clock = sandbox.useFakeTimers();
message = await getTestMessage("EXTENDED_TRIPLETS_1");
fakeDoc = {
body: document.createElement("body"),
head: document.createElement("head"),
createElement: type => document.createElement(type),
getElementById: () => document.createElement("div"),
activeElement: document.createElement("div"),
};
onBlockByIdStub = sandbox.stub();
sandbox
.stub(global, "fetch")
.withArgs("http://fake.com/endpoint")
.resolves({
ok: true,
status: 200,
json: () => Promise.resolve(FAKE_FLOW_PARAMS),
});
wrapper = mount(
<FirstRun
message={message}
document={fakeDoc}
dispatch={() => {}}
sendUserActionTelemetry={() => {}}
onBlockById={onBlockByIdStub}
/>
);
}
beforeEach(setup);
afterEach(() => {
sandbox.restore();
});
it("should render", () => {
assert.ok(wrapper);
});
describe("with triplets", () => {
it("should render triplets", () => {
assert.lengthOf(wrapper.find(Triplets), 1, "<Triplets>");
});
it("should show the card panel and the content on the Triplets", () => {
const tripletsProps = wrapper.find(Triplets).props();
assert.propertyVal(tripletsProps, "showCardPanel", true);
});
it("should set the UTM term to trailhead-cards-card (for the extended-triplet message)", () => {
const tProps = wrapper.find(Triplets).props();
assert.propertyVal(tProps, "UTMTerm", "trailhead-cards-card");
});
});
describe("with no triplets", () => {
it("should render empty", () => {
message = { type: "FOO_123" };
wrapper = mount(<FirstRun message={message} document={fakeDoc} />);
assert.isTrue(wrapper.isEmptyRender());
});
});
it("should pass along executeAction appropriately", () => {
const stub = sandbox.stub();
wrapper = mount(
<FirstRun message={message} document={fakeDoc} executeAction={stub} />
);
assert.propertyVal(wrapper.find(Triplets).props(), "onAction", stub);
});
it("should load flow params on mount if fxaEndpoint is defined", () => {
const stub = sandbox.stub();
wrapper = mount(
<FirstRun
message={message}
document={fakeDoc}
fetchFlowParams={stub}
fxaEndpoint="https://foo.com"
/>
);
assert.calledOnce(stub);
});
it("should load flow params onUpdate if fxaEndpoint is not defined on mount and then later defined", () => {
const stub = sandbox.stub();
wrapper = mount(
<FirstRun message={message} document={fakeDoc} fetchFlowParams={stub} />
);
assert.notCalled(stub);
wrapper.setProps({ fxaEndpoint: "https://foo.com" });
assert.calledOnce(stub);
});
it("should not load flow params again onUpdate if they were already set", () => {
const stub = sandbox.stub();
wrapper = mount(
<FirstRun
message={message}
document={fakeDoc}
fetchFlowParams={stub}
fxaEndpoint="https://foo.com"
/>
);
wrapper.setProps({ foo: "bar" });
wrapper.setProps({ foo: "baz" });
assert.calledOnce(stub);
});
it("should load fluent files on mount", () => {
assert.lengthOf(fakeDoc.head.querySelectorAll("link"), FLUENT_FILES.length);
});
it("should update didUserClearTriplets state to true on close of triplet", () => {
assert.isFalse(wrapper.state().didUserClearTriplets);
// Simulate calling close Triplets
wrapper
.find(Triplets)
.find(".icon-dismiss")
.simulate("click");
assert.isTrue(wrapper.state().didUserClearTriplets);
});
it("should hide triplets when closeTriplets is called and block extended triplets after 500ms", () => {
// Simulate calling next scene
wrapper
.find(Triplets)
.find(".icon-dismiss")
.simulate("click");
assert.isFalse(
wrapper
.find(Triplets)
.find(".trailheadCardGrid")
.hasClass("show"),
"Show triplet content"
);
assert.notCalled(onBlockByIdStub);
clock.tick(500);
assert.calledWith(onBlockByIdStub, "EXTENDED_TRIPLETS_1");
});
it("should update triplets card when cards in message bundle changes", async () => {
let tripletsProps = wrapper.find(Triplets).props();
assert.propertyVal(tripletsProps, "cards", FAKE_TRIPLETS_BUNDLE_1);
const messageWithNewBundle = await getTestMessage("TRAILHEAD_1", true);
wrapper.setProps({ message: messageWithNewBundle });
tripletsProps = wrapper.find(Triplets).props();
assert.propertyVal(tripletsProps, "cards", FAKE_TRIPLETS_BUNDLE_2);
});
});

View File

@ -1,156 +0,0 @@
import { mount } from "enzyme";
import { Triplets } from "content-src/asrouter/templates/FirstRun/Triplets";
import { OnboardingCard } from "content-src/asrouter/templates/OnboardingMessage/OnboardingMessage";
import React from "react";
const CARDS = [
{
id: "CARD_1",
content: {
title: { string_id: "onboarding-private-browsing-title" },
text: { string_id: "onboarding-private-browsing-text" },
icon: "icon",
primary_button: {
label: { string_id: "onboarding-button-label-get-started" },
action: {
type: "OPEN_URL",
data: { args: "https://example.com/" },
},
},
},
},
{
id: "CARD_2",
content: {
title: "Test title",
text: "Test body text",
icon: "icon",
primary_button: {
label: "Test button label",
action: {
type: "OPEN_URL",
data: { args: "https://example.com/" },
},
},
},
},
{
id: "CARD_3",
content: {
title: "Test title with no body text",
icon: "icon",
primary_button: {
label: "Test button label with no body text",
action: {
type: "OPEN_URL",
data: { args: "https://example.com/" },
},
},
},
},
];
describe("<Triplets>", () => {
let wrapper;
let sandbox;
let sendTelemetryStub;
let onAction;
let onHide;
let onBlockById;
async function setup() {
sandbox = sinon.createSandbox();
sendTelemetryStub = sandbox.stub();
onAction = sandbox.stub();
onBlockById = sandbox.stub();
onHide = sandbox.stub();
wrapper = mount(
<Triplets
cards={CARDS}
showCardPanel={true}
showContent={true}
hideContainer={onHide}
onAction={onAction}
UTMTerm="trailhead-join-card"
onBlockById={onBlockById}
sendUserActionTelemetry={sendTelemetryStub}
/>
);
}
beforeEach(setup);
afterEach(() => {
sandbox.restore();
});
it("should add an expanded class to container if props.showCardPanel is true", () => {
wrapper.setProps({ showCardPanel: true });
assert.isTrue(
wrapper.find(".trailheadCards").hasClass("expanded"),
"has .expanded)"
);
});
it("should add a collapsed class to container if props.showCardPanel is true", () => {
wrapper.setProps({ showCardPanel: false });
assert.isFalse(
wrapper.find(".trailheadCards").hasClass("expanded"),
"has .expanded)"
);
});
it("should send telemetry and call props.hideContainer when the dismiss button is clicked", () => {
wrapper.find("button.icon-dismiss").simulate("click");
assert.calledOnce(onHide);
assert.calledWith(sendTelemetryStub, {
event: "DISMISS",
message_id: `${CARDS[0].id},${CARDS[1].id},${CARDS[2].id}`,
id: "onboarding-cards",
action: "onboarding_user_event",
});
});
it("should add utm_* query params to card actions and send the right ping when a card button is clicked", () => {
wrapper
.find(OnboardingCard)
.first()
.find("button.onboardingButton")
.simulate("click");
assert.calledOnce(onAction);
const url = onAction.firstCall.args[0].data.args;
assert.equal(
url,
"https://example.com/?utm_source=activity-stream&utm_campaign=firstrun&utm_medium=referral&utm_term=trailhead-join-card"
);
assert.calledWith(sendTelemetryStub, {
event: "CLICK_BUTTON",
message_id: CARDS[0].id,
id: "TRAILHEAD",
});
});
it("should not call blockById by default when a card button is clicked", () => {
wrapper
.find(OnboardingCard)
.first()
.find("button.onboardingButton")
.simulate("click");
assert.notCalled(onBlockById);
});
it("should call blockById when blockOnClick on message is true", () => {
CARDS[0].blockOnClick = true;
wrapper
.find(OnboardingCard)
.first()
.find("button.onboardingButton")
.simulate("click");
assert.calledOnce(onBlockById);
assert.calledWith(onBlockById, CARDS[0].id);
});
it("Onboarding card without text content should hide onboardingText paragraph", () => {
assert.lengthOf(
wrapper
.find(OnboardingCard)
.last()
.find("p.onboardingText"),
0
);
});
});

View File

@ -75,7 +75,7 @@ describe("ASRouterAdmin", () => {
assert.equal(
wrapper
.find("h2")
.at(2)
.at(1)
.text(),
"Messages"
);

View File

@ -11,9 +11,6 @@
onboarding-welcome-header = Welcome to { -brand-short-name }
onboarding-start-browsing-button-label = Start Browsing
onboarding-not-now-button-label = Not now
onboarding-cards-dismiss =
.title = Dismiss
.aria-label = Dismiss
## Custom Return To AMO onboarding strings
@ -120,62 +117,3 @@ onboarding-multistage-theme-description-alpenglow =
.aria-description =
Use a colorful appearance for buttons,
menus, and windows.
## These strings belong to the individual onboarding messages.
## 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.
## The string for the button is found above, in the UI strings section
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-button2 = How it Works
onboarding-data-sync-title = Take Your Settings with You
# "Sync" is short for synchronize.
onboarding-data-sync-text2 = Sync your bookmarks, passwords, and more everywhere you use { -brand-product-name }.
onboarding-data-sync-button2 = Sign in to { -sync-brand-short-name }
onboarding-firefox-monitor-title = Stay Alert to Data Breaches
onboarding-firefox-monitor-text2 = { -monitor-brand-name } monitors if your email has appeared in a known data breach and alerts you if it appears in a new breach.
onboarding-firefox-monitor-button = Sign Up for Alerts
onboarding-browse-privately-title = Browse Privately
onboarding-browse-privately-text = Private Browsing clears your search and browsing history to keep it secret from anyone who uses your computer.
onboarding-browse-privately-button = Open a Private Window
onboarding-firefox-send-title = Keep Your Shared Files Private
onboarding-firefox-send-text2 = Upload your files to { -send-brand-name } to share them with end-to-end encryption and a link that automatically expires.
onboarding-firefox-send-button = Try { -send-brand-name }
onboarding-mobile-phone-title = Get { -brand-product-name } on Your Phone
onboarding-mobile-phone-text = Download { -brand-product-name } for iOS or Android and sync your data across devices.
# "Mobile" is short for mobile/cellular phone, "Browser" is short for web
# browser.
onboarding-mobile-phone-button = Download Mobile Browser
onboarding-send-tabs-title = Instantly Send Yourself Tabs
# "Send Tabs" refers to "Send Tab to Device" feature that appears when opening a
# tab's context menu.
onboarding-send-tabs-text2 = Easily share pages between your devices without having to copy links or leave the browser.
onboarding-send-tabs-button = Start Using Send Tabs
onboarding-pocket-anywhere-title = Read and Listen Anywhere
onboarding-pocket-anywhere-text2 = Save your favorite content offline with the { -pocket-brand-name } App and read, listen, and watch whenever its convenient for you.
onboarding-pocket-anywhere-button = Try { -pocket-brand-name }
onboarding-lockwise-strong-passwords-title = Create and Store Strong Passwords
onboarding-lockwise-strong-passwords-text = { -lockwise-brand-name } creates strong passwords on the spot and saves all of them in one place.
onboarding-lockwise-strong-passwords-button = Manage Your Logins
onboarding-facebook-container-title = Set Boundaries with Facebook
onboarding-facebook-container-text2 = { -facebook-container-brand-name } keeps your profile separate from everything else, making it harder for Facebook to target you with ads.
onboarding-facebook-container-button = Add the Extension
onboarding-import-browser-settings-title = Import Your Bookmarks, Passwords, and More
onboarding-import-browser-settings-text = Dive right in—easily bring your Chrome sites and settings with you.
onboarding-import-browser-settings-button = Import Chrome Data
onboarding-personal-data-promise-title = Private by Design
onboarding-personal-data-promise-text = { -brand-product-name } treats your data with respect by taking less of it, protecting it, and being clear about how we use it.
onboarding-personal-data-promise-button = Read our Promise