Bug 1637079 - Initial multi stage about:welcome layout r=k88hudson

Differential Revision: https://phabricator.services.mozilla.com/D74811
This commit is contained in:
Punam Dahiya 2020-05-16 00:02:11 +00:00
Родитель 656bed86b0
Коммит d2bfedea15
13 изменённых файлов: 452 добавлений и 158 удалений

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

@ -1327,6 +1327,8 @@ pref("trailhead.firstrun.branches", "join-dynamic");
// Separate about welcome // Separate about welcome
pref("browser.aboutwelcome.enabled", true); pref("browser.aboutwelcome.enabled", true);
// Used for switching simplified 3 cards welcome to multistage welcome
pref("browser.aboutwelcome.overrideContent", "");
// The pref that controls if the What's New panel is enabled. // The pref that controls if the What's New panel is enabled.
pref("browser.messaging-system.whatsNewPanel.enabled", true); pref("browser.messaging-system.whatsNewPanel.enabled", true);

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

@ -39,6 +39,7 @@ module.exports = {
// These files use fluent-dom to insert content // These files use fluent-dom to insert content
files: [ files: [
"content-src/aboutwelcome/components/HeroText.jsx", "content-src/aboutwelcome/components/HeroText.jsx",
"content-src/aboutwelcome/components/MultiStageAboutWelcome.jsx",
"content-src/asrouter/templates/OnboardingMessage/**", "content-src/asrouter/templates/OnboardingMessage/**",
"content-src/asrouter/templates/FirstRun/**", "content-src/asrouter/templates/FirstRun/**",
"content-src/asrouter/templates/Trailhead/**", "content-src/asrouter/templates/Trailhead/**",

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

@ -21,6 +21,25 @@ XPCOMUtils.defineLazyGetter(this, "log", () => {
return new Logger("AboutWelcomeChild"); return new Logger("AboutWelcomeChild");
}); });
function _parseOverrideContent(value) {
let result = {};
try {
result = value ? JSON.parse(value) : {};
} catch (e) {
Cu.reportError(e);
}
return result;
}
XPCOMUtils.defineLazyPreferenceGetter(
this,
"multiStageAboutWelcomeContent",
"browser.aboutwelcome.overrideContent",
"",
null,
_parseOverrideContent
);
class AboutWelcomeChild extends JSWindowActorChild { class AboutWelcomeChild extends JSWindowActorChild {
actorCreated() { actorCreated() {
this.exportFunctions(); this.exportFunctions();
@ -73,6 +92,12 @@ class AboutWelcomeChild extends JSWindowActorChild {
defineAs: "AWGetStartupData", defineAs: "AWGetStartupData",
}); });
// For local dev, checks for JSON content inside pref browser.aboutwelcome.overrideContent
// that is used to override default 3 cards welcome UI with multistage welcome
Cu.exportFunction(this.AWGetMultiStageScreens.bind(this), window, {
defineAs: "AWGetMultiStageScreens",
});
Cu.exportFunction(this.AWGetFxAMetricsFlowURI.bind(this), window, { Cu.exportFunction(this.AWGetFxAMetricsFlowURI.bind(this), window, {
defineAs: "AWGetFxAMetricsFlowURI", defineAs: "AWGetFxAMetricsFlowURI",
}); });
@ -92,6 +117,16 @@ class AboutWelcomeChild extends JSWindowActorChild {
); );
} }
/**
* Send multistage welcome JSON data read from aboutwelcome.overrideConetent pref to page
*/
AWGetMultiStageScreens() {
return Cu.cloneInto(
multiStageAboutWelcomeContent || {},
this.contentWindow
);
}
/** /**
* Send initial data to page including experiment information * Send initial data to page including experiment information
*/ */

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

@ -100,10 +100,11 @@ __webpack_require__.r(__webpack_exports__);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2); /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(2);
/* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__); /* harmony import */ var react_dom__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(react_dom__WEBPACK_IMPORTED_MODULE_1__);
/* harmony import */ var _components_HeroText__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); /* harmony import */ var _components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3);
/* harmony import */ var _components_FxCards__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5); /* harmony import */ var _components_HeroText__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6);
/* harmony import */ var _components_MSLocalized__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(4); /* harmony import */ var _components_FxCards__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7);
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(8); /* harmony import */ var _components_MSLocalized__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(4);
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(5);
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
@ -116,6 +117,7 @@ function _extends() { _extends = Object.assign || function (target) { for (var i
class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent { class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent {
constructor(props) { constructor(props) {
super(props); super(props);
@ -136,6 +138,8 @@ class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComp
componentDidMount() { componentDidMount() {
if (this.props.experiment && this.props.branchId) { if (this.props.experiment && this.props.branchId) {
this.messageId = `ABOUT_WELCOME_${this.props.experiment}_${this.props.branchId}`.toUpperCase(); this.messageId = `ABOUT_WELCOME_${this.props.experiment}_${this.props.branchId}`.toUpperCase();
} else if (this.props.id && this.props.screens) {
this.messageId = this.props.id;
} }
this.fetchFxAFlowUri(); this.fetchFxAFlowUri();
@ -149,7 +153,7 @@ class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComp
} }
handleStartBtnClick() { handleStartBtnClick() {
_lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_5__["AboutWelcomeUtils"].handleUserAction(this.props.startButton.action); _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_6__["AboutWelcomeUtils"].handleUserAction(this.props.startButton.action);
const ping = { const ping = {
event: "CLICK_BUTTON", event: "CLICK_BUTTON",
event_context: { event_context: {
@ -165,21 +169,31 @@ class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComp
render() { render() {
const { const {
props props
} = this; } = this; // TBD: Refactor to redirect based off template value
// inside props.template
// Create SimpleAboutWelcome that renders default about welcome
// See Bug 1638087
if (props.screens) {
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_MultiStageAboutWelcome__WEBPACK_IMPORTED_MODULE_2__["MultiStageAboutWelcome"], {
screens: props.screens
});
}
let UTMTerm = this.props.experiment && this.props.branchId ? `${this.props.experiment}-${this.props.branchId}` : "default"; let UTMTerm = this.props.experiment && this.props.branchId ? `${this.props.experiment}-${this.props.branchId}` : "default";
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
className: "outer-wrapper welcomeContainer" className: "outer-wrapper welcomeContainer"
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", { }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
className: "welcomeContainerInner" className: "welcomeContainerInner"
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("main", null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_HeroText__WEBPACK_IMPORTED_MODULE_2__["HeroText"], { }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("main", null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_HeroText__WEBPACK_IMPORTED_MODULE_3__["HeroText"], {
title: props.title, title: props.title,
subtitle: props.subtitle subtitle: props.subtitle
}), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_FxCards__WEBPACK_IMPORTED_MODULE_3__["FxCards"], { }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_FxCards__WEBPACK_IMPORTED_MODULE_4__["FxCards"], {
cards: props.cards, cards: props.cards,
metricsFlowUri: this.state.metricsFlowUri, metricsFlowUri: this.state.metricsFlowUri,
sendTelemetry: window.AWSendEventTelemetry, sendTelemetry: window.AWSendEventTelemetry,
utm_term: UTMTerm utm_term: UTMTerm
}), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_MSLocalized__WEBPACK_IMPORTED_MODULE_4__["Localized"], { }), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_components_MSLocalized__WEBPACK_IMPORTED_MODULE_5__["Localized"], {
text: props.startButton.label text: props.startButton.label
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", { }, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", {
className: "start-button", className: "start-button",
@ -189,14 +203,20 @@ class AboutWelcome extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComp
} }
AboutWelcome.defaultProps = _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_5__["DEFAULT_WELCOME_CONTENT"]; AboutWelcome.defaultProps = _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_6__["DEFAULT_WELCOME_CONTENT"];
async function mount() { async function mount() {
const { const {
slug, slug,
branch branch
} = await window.AWGetStartupData(); } = await window.AWGetStartupData();
const settings = branch && branch.value ? branch.value : {}; let settings = branch && branch.value ? branch.value : {};
if (!(branch && branch.value)) {
// Check for override content in pref browser.aboutwelcome.overrideContent
settings = await window.AWGetMultiStageScreens();
}
react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render(react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(AboutWelcome, _extends({ react_dom__WEBPACK_IMPORTED_MODULE_1___default.a.render(react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(AboutWelcome, _extends({
experiment: slug, experiment: slug,
branchId: branch && branch.slug branchId: branch && branch.slug
@ -223,24 +243,48 @@ module.exports = ReactDOM;
"use strict"; "use strict";
__webpack_require__.r(__webpack_exports__); __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HeroText", function() { return HeroText; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "MultiStageAboutWelcome", function() { return MultiStageAboutWelcome; });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4); /* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5);
/* This Source Code Form is subject to the terms of the Mozilla Public /* 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, * 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/. */ * You can obtain one at http://mozilla.org/MPL/2.0/. */
const HeroText = props => {
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { const MultiStageAboutWelcome = props => {
text: props.title const [index, setScreenIndex] = Object(react__WEBPACK_IMPORTED_MODULE_0__["useState"])(0); // Transition to next screen, opening about:home on last screen button CTA
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", {
className: "welcome-title" const handleTransition = index < props.screens.length ? Object(react__WEBPACK_IMPORTED_MODULE_0__["useCallback"])(() => setScreenIndex(prevState => prevState + 1), []) : _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_2__["AboutWelcomeUtils"].handleUserAction({
})), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], { type: "OPEN_ABOUT_PAGE",
text: props.subtitle data: {
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h2", { args: "home",
className: "welcome-subtitle" where: "current"
}
});
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
className: `welcomeCardGrid`
}, props.screens.map(screen => {
return index === screen.order ? react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(WelcomeScreen, {
key: screen.id,
id: screen.id,
content: screen.content,
navigate: handleTransition
}) : null;
})));
};
const WelcomeScreen = props => {
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("div", {
className: `${props.id}`
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], {
text: props.content.title
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", null)), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], {
text: props.content.primary_button.label
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("button", {
onClick: props.navigate
}))); })));
}; };
@ -305,14 +349,164 @@ const Localized = ({
/* 5 */ /* 5 */
/***/ (function(module, __webpack_exports__, __webpack_require__) { /***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AboutWelcomeUtils", function() { return AboutWelcomeUtils; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DEFAULT_WELCOME_CONTENT", function() { return DEFAULT_WELCOME_CONTENT; });
/* 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/. */
const AboutWelcomeUtils = {
handleUserAction(action) {
switch (action.type) {
case "OPEN_ABOUT_PAGE":
case "OPEN_AWESOME_BAR":
case "OPEN_PRIVATE_BROWSER_WINDOW":
case "SHOW_MIGRATION_WIZARD":
window.AWSendToParent("SPECIAL_ACTION", action);
break;
case "OPEN_URL":
window.open(action.data.args);
break;
}
},
sendEvent(type, detail) {
document.dispatchEvent(new CustomEvent(`AWPage:${type}`, {
bubbles: true,
detail
}));
}
};
const DEFAULT_WELCOME_CONTENT = {
title: {
string_id: "onboarding-welcome-header"
},
startButton: {
label: {
string_id: "onboarding-start-browsing-button-label"
},
message_id: "START_BROWSING_BUTTON",
action: {
type: "OPEN_AWESOME_BAR"
}
},
cards: [{
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"
}
}
}
},
id: "TRAILHEAD_CARD_2",
order: 1,
blockOnClick: false
}, {
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"
}
}
}
},
id: "TRAILHEAD_CARD_3",
order: 2,
blockOnClick: false
}, {
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"
}
}
},
id: "TRAILHEAD_CARD_4",
order: 3,
blockOnClick: true
}]
};
/***/ }),
/* 6 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "HeroText", function() { return HeroText; });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(4);
/* 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/. */
const HeroText = props => {
return react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(react__WEBPACK_IMPORTED_MODULE_0___default.a.Fragment, null, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], {
text: props.title
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h1", {
className: "welcome-title"
})), react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__["Localized"], {
text: props.subtitle
}, react__WEBPACK_IMPORTED_MODULE_0___default.a.createElement("h2", {
className: "welcome-subtitle"
})));
};
/***/ }),
/* 7 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict"; "use strict";
__webpack_require__.r(__webpack_exports__); __webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FxCards", function() { return FxCards; }); /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "FxCards", function() { return FxCards; });
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(1);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__); /* harmony import */ var react__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(react__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(6); /* harmony import */ var _asrouter_templates_FirstRun_addUtmParams__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(8);
/* harmony import */ var _asrouter_templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(7); /* harmony import */ var _asrouter_templates_OnboardingMessage_OnboardingMessage__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(9);
/* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(8); /* harmony import */ var _lib_aboutwelcome_utils__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(5);
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
/* This Source Code Form is subject to the terms of the Mozilla Public /* This Source Code Form is subject to the terms of the Mozilla Public
@ -422,7 +616,7 @@ class FxCards extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureComponent
} }
/***/ }), /***/ }),
/* 6 */ /* 8 */
/***/ (function(module, __webpack_exports__, __webpack_require__) { /***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict"; "use strict";
@ -463,7 +657,7 @@ function addUtmParams(url, utmTerm) {
} }
/***/ }), /***/ }),
/* 7 */ /* 9 */
/***/ (function(module, __webpack_exports__, __webpack_require__) { /***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict"; "use strict";
@ -527,127 +721,5 @@ class OnboardingCard extends react__WEBPACK_IMPORTED_MODULE_0___default.a.PureCo
} }
/***/ }),
/* 8 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "AboutWelcomeUtils", function() { return AboutWelcomeUtils; });
/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "DEFAULT_WELCOME_CONTENT", function() { return DEFAULT_WELCOME_CONTENT; });
/* 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/. */
const AboutWelcomeUtils = {
handleUserAction(action) {
switch (action.type) {
case "OPEN_AWESOME_BAR":
case "OPEN_PRIVATE_BROWSER_WINDOW":
case "SHOW_MIGRATION_WIZARD":
window.AWSendToParent("SPECIAL_ACTION", action);
break;
case "OPEN_URL":
window.open(action.data.args);
break;
}
},
sendEvent(type, detail) {
document.dispatchEvent(new CustomEvent(`AWPage:${type}`, {
bubbles: true,
detail
}));
}
};
const DEFAULT_WELCOME_CONTENT = {
title: {
string_id: "onboarding-welcome-header"
},
startButton: {
label: {
string_id: "onboarding-start-browsing-button-label"
},
message_id: "START_BROWSING_BUTTON",
action: {
type: "OPEN_AWESOME_BAR"
}
},
cards: [{
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"
}
}
}
},
id: "TRAILHEAD_CARD_2",
order: 1,
blockOnClick: false
}, {
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"
}
}
}
},
id: "TRAILHEAD_CARD_3",
order: 2,
blockOnClick: false
}, {
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"
}
}
},
id: "TRAILHEAD_CARD_4",
order: 3,
blockOnClick: true
}]
};
/***/ }) /***/ })
/******/ ]); /******/ ]);

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

@ -121,12 +121,9 @@ body {
margin-top: 32px; margin-top: 32px;
display: grid; display: grid;
grid-gap: 32px; grid-gap: 32px;
opacity: 0;
transition: opacity 0.4s; transition: opacity 0.4s;
transition-delay: 0.1s; transition-delay: 0.1s;
grid-auto-rows: 1fr; } grid-auto-rows: 1fr; }
.welcomeCardGrid.show {
opacity: 1; }
@media (min-width: 610px) { @media (min-width: 610px) {
.welcomeCardGrid { .welcomeCardGrid {
grid-template-columns: repeat(auto-fit, 224px); } } grid-template-columns: repeat(auto-fit, 224px); } }

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

@ -4,6 +4,7 @@
import React from "react"; import React from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import { MultiStageAboutWelcome } from "./components/MultiStageAboutWelcome";
import { HeroText } from "./components/HeroText"; import { HeroText } from "./components/HeroText";
import { FxCards } from "./components/FxCards"; import { FxCards } from "./components/FxCards";
import { Localized } from "./components/MSLocalized"; import { Localized } from "./components/MSLocalized";
@ -29,6 +30,8 @@ class AboutWelcome extends React.PureComponent {
componentDidMount() { componentDidMount() {
if (this.props.experiment && this.props.branchId) { if (this.props.experiment && this.props.branchId) {
this.messageId = `ABOUT_WELCOME_${this.props.experiment}_${this.props.branchId}`.toUpperCase(); this.messageId = `ABOUT_WELCOME_${this.props.experiment}_${this.props.branchId}`.toUpperCase();
} else if (this.props.id && this.props.screens) {
this.messageId = this.props.id;
} }
this.fetchFxAFlowUri(); this.fetchFxAFlowUri();
window.AWSendEventTelemetry({ window.AWSendEventTelemetry({
@ -56,6 +59,14 @@ class AboutWelcome extends React.PureComponent {
render() { render() {
const { props } = this; const { props } = this;
// TBD: Refactor to redirect based off template value
// inside props.template
// Create SimpleAboutWelcome that renders default about welcome
// See Bug 1638087
if (props.screens) {
return <MultiStageAboutWelcome screens={props.screens} />;
}
let UTMTerm = let UTMTerm =
this.props.experiment && this.props.branchId this.props.experiment && this.props.branchId
? `${this.props.experiment}-${this.props.branchId}` ? `${this.props.experiment}-${this.props.branchId}`
@ -88,7 +99,12 @@ AboutWelcome.defaultProps = DEFAULT_WELCOME_CONTENT;
async function mount() { async function mount() {
const { slug, branch } = await window.AWGetStartupData(); const { slug, branch } = await window.AWGetStartupData();
const settings = branch && branch.value ? branch.value : {}; let settings = branch && branch.value ? branch.value : {};
if (!(branch && branch.value)) {
// Check for override content in pref browser.aboutwelcome.overrideContent
settings = await window.AWGetMultiStageScreens();
}
ReactDOM.render( ReactDOM.render(
<AboutWelcome <AboutWelcome

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

@ -61,15 +61,10 @@ body {
margin-top: 32px; margin-top: 32px;
display: grid; display: grid;
grid-gap: 32px; grid-gap: 32px;
opacity: 0;
transition: opacity 0.4s; transition: opacity 0.4s;
transition-delay: 0.1s; transition-delay: 0.1s;
grid-auto-rows: 1fr; grid-auto-rows: 1fr;
&.show {
opacity: 1;
}
@media (min-width: $break-point-medium) { @media (min-width: $break-point-medium) {
grid-template-columns: repeat(auto-fit, 224px); grid-template-columns: repeat(auto-fit, 224px);
} }

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

@ -0,0 +1,49 @@
/* 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, { useState, useCallback } from "react";
import { Localized } from "./MSLocalized";
import { AboutWelcomeUtils } from "../../lib/aboutwelcome-utils";
export const MultiStageAboutWelcome = props => {
const [index, setScreenIndex] = useState(0);
// Transition to next screen, opening about:home on last screen button CTA
const handleTransition =
index < props.screens.length
? useCallback(() => setScreenIndex(prevState => prevState + 1), [])
: AboutWelcomeUtils.handleUserAction({
type: "OPEN_ABOUT_PAGE",
data: { args: "home", where: "current" },
});
return (
<React.Fragment>
<div className={`welcomeCardGrid`}>
{props.screens.map(screen => {
return index === screen.order ? (
<WelcomeScreen
key={screen.id}
id={screen.id}
content={screen.content}
navigate={handleTransition}
/>
) : null;
})}
</div>
</React.Fragment>
);
};
const WelcomeScreen = props => {
return (
<div className={`${props.id}`}>
<Localized text={props.content.title}>
<h1 />
</Localized>
<Localized text={props.content.primary_button.label}>
<button onClick={props.navigate} />
</Localized>
</div>
);
};

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

@ -5,6 +5,7 @@
export const AboutWelcomeUtils = { export const AboutWelcomeUtils = {
handleUserAction(action) { handleUserAction(action) {
switch (action.type) { switch (action.type) {
case "OPEN_ABOUT_PAGE":
case "OPEN_AWESOME_BAR": case "OPEN_AWESOME_BAR":
case "OPEN_PRIVATE_BROWSER_WINDOW": case "OPEN_PRIVATE_BROWSER_WINDOW":
case "SHOW_MIGRATION_WIZARD": case "SHOW_MIGRATION_WIZARD":

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

@ -16,6 +16,7 @@ prefs =
[browser_aboutwelcome.js] [browser_aboutwelcome.js]
[browser_aboutwelcome_actors.js] [browser_aboutwelcome_actors.js]
[browser_aboutwelcome_simplified.js] [browser_aboutwelcome_simplified.js]
[browser_aboutwelcome_multistage.js]
[browser_aboutwelcome_observer.js] [browser_aboutwelcome_observer.js]
[browser_as_load_location.js] [browser_as_load_location.js]
[browser_as_render.js] [browser_as_render.js]

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

@ -0,0 +1,116 @@
"use strict";
const SEPARATE_ABOUT_WELCOME_PREF = "browser.aboutwelcome.enabled";
const ABOUT_WELCOME_OVERRIDE_CONTENT_PREF =
"browser.aboutwelcome.overrideContent";
const TEST_MULTISTAGE_JSON =
'{"id": "multi-stage-welcome","screens": [{"id": "AW_STEP1","order": 0,"content": {"title": "Step 1","primary_button": {"label": "Next"}}},{"id": "AW_STEP2","order": 1,"content": {"title": "Step 2","primary_button": {"label": "Next"}}},{"id": "AW_STEP3","order": 2,"content": {"title": "Step 3","primary_button": {"label": "Next"}}}]}';
/**
* Sets the aboutwelcome pref to enabled simplified welcome UI
*/
async function setAboutWelcomePref(value) {
return pushPrefs([SEPARATE_ABOUT_WELCOME_PREF, value]);
}
async function setAboutWelcomeMultiStage(value) {
return pushPrefs([ABOUT_WELCOME_OVERRIDE_CONTENT_PREF, value]);
}
async function openAboutWelcome() {
await setAboutWelcomePref(true);
await setAboutWelcomeMultiStage(TEST_MULTISTAGE_JSON);
let tab = await BrowserTestUtils.openNewForegroundTab(
gBrowser,
"about:welcome",
true
);
registerCleanupFunction(() => {
BrowserTestUtils.removeTab(tab);
});
return tab.linkedBrowser;
}
/**
* Setup and test simplified welcome UI
*/
async function test_about_welcome(
browser,
experiment,
expectedSelectors = [],
unexpectedSelectors = []
) {
await ContentTask.spawn(
browser,
{ expectedSelectors, experiment, unexpectedSelectors },
async ({
expectedSelectors: expected,
experiment: experimentName,
unexpectedSelectors: unexpected,
}) => {
for (let selector of expected) {
await ContentTaskUtils.waitForCondition(
() => content.document.querySelector(selector),
`Should render ${selector} in ${experimentName}`
);
}
for (let selector of unexpected) {
ok(
!content.document.querySelector(selector),
`Should not render ${selector} in ${experimentName}`
);
}
}
);
}
async function onNavigate(browser, message) {
await ContentTask.spawn(
browser,
{ message },
async ({ message: messageText }) => {
await ContentTaskUtils.waitForCondition(
() => content.document.querySelector("button"),
messageText
);
let button = content.document.querySelector("button");
button.click();
}
);
}
/**
* Test the multistage welcome UI rendered using TEST_MULTISTAGE_JSON
*/
add_task(async function test_Separate_About_Welcome_branches() {
let browser = await openAboutWelcome();
await test_about_welcome(
browser,
"multistage step 1",
// Expected selectors:
["div.welcomeCardGrid", "div.AW_STEP1"],
// Unexpected selectors:
["div.AW_STEP2", "div.AW_STEP3"]
);
await onNavigate(browser, "Step 1");
await test_about_welcome(
browser,
"multistage step 2",
// Expected selectors:
["div.welcomeCardGrid", "div.AW_STEP2"],
// Unexpected selectors:
["div.AW_STEP1", "div.AW_STEP3"]
);
await onNavigate(browser, "Step 2");
await test_about_welcome(
browser,
"multistage step 3",
// Expected selectors:
["div.welcomeCardGrid", "div.AW_STEP3"],
// Unexpected selectors:
["div.AW_STEP1", "div.AW_STEP2"]
);
});

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

@ -110,7 +110,10 @@ const SpecialMessageActions = {
if (action.data.entrypoint) { if (action.data.entrypoint) {
aboutPageURL.search = action.data.entrypoint; aboutPageURL.search = action.data.entrypoint;
} }
window.openTrustedLinkIn(aboutPageURL.toString(), "tab"); window.openTrustedLinkIn(
aboutPageURL.toString(),
action.data.where || "tab"
);
break; break;
case "OPEN_PREFERENCES_PAGE": case "OPEN_PREFERENCES_PAGE":
window.openPreferences( window.openPreferences(

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

@ -66,6 +66,12 @@ const SpecialMessageActionSchemas = {
description: 'The about page. E.g. "welcome" for about:welcome', description: 'The about page. E.g. "welcome" for about:welcome',
type: "string", type: "string",
}, },
where: {
default: "tab",
description: "Where the URL is opened.",
enum: ["current", "save", "tab", "tabshifted", "window"],
type: "string",
},
entrypoint: { entrypoint: {
description: description:
'Any optional entrypoint value that will be added to the search. E.g. "foo=bar" would result in about:welcome?foo=bar', 'Any optional entrypoint value that will be added to the search. E.g. "foo=bar" would result in about:welcome?foo=bar',