commit ca0244c0aee372a0bd7f77bce00aef7a5b25637c parent 6ec37a454bffd6ca9a89f3a6541e0dc05d8dda3b Author: hanna.alemu <halemu@mozilla.com> Date: Tue, 7 Oct 2025 19:49:52 +0000 Bug 1979439 - Embedded Fx Backup r=omc-reviewers,fluent-reviewers,bolsson,mconley,aminomancer Differential Revision: https://phabricator.services.mozilla.com/D261827 Diffstat:
22 files changed, 936 insertions(+), 254 deletions(-)
diff --git a/browser/base/content/spotlight.html b/browser/base/content/spotlight.html @@ -8,7 +8,7 @@ <head> <meta http-equiv="Content-Security-Policy" - content="default-src resource: chrome:; img-src https://www.mozilla.org https://firefox-settings-attachments.cdn.mozilla.net https://addons.mozilla.org blob: chrome:; style-src resource: chrome: 'unsafe-inline'; object-src 'none'" + content="default-src resource: chrome:; img-src moz-icon: https://www.mozilla.org https://firefox-settings-attachments.cdn.mozilla.net https://addons.mozilla.org blob: chrome:; style-src resource: chrome: 'unsafe-inline'; object-src 'none'" /> <meta name="referrer" content="no-referrer" /> <link @@ -25,6 +25,7 @@ <link rel="localization" href="browser/migrationWizard.ftl" /> <link rel="localization" href="browser/preonboarding.ftl" /> <link rel="localization" href="browser/termsofuse.ftl" /> + <link rel="localization" href="preview/backupSettings.ftl" /> <link rel="localization" href="browser/featureCallout.ftl" /> </head> <body @@ -41,5 +42,9 @@ src="chrome://browser/content/migration/migration-wizard.mjs" type="module" ></script> + <script + src="chrome://browser/content/backup/turn-on-scheduled-backups.mjs" + type="module" + ></script> </body> </html> diff --git a/browser/components/DesktopActorRegistry.sys.mjs b/browser/components/DesktopActorRegistry.sys.mjs @@ -240,7 +240,14 @@ let JSWINDOWACTORS = { "BackupUI:EditBackupLocation": { wantUntrusted: true }, }, }, - matches: ["about:preferences*", "about:settings*", "about:welcome*"], + includeChrome: true, + allFrames: true, + matches: [ + "about:preferences*", + "about:settings*", + "about:welcome*", + "chrome://browser/content/spotlight.html", + ], enablePreference: "browser.backup.preferences.ui.enabled", }, diff --git a/browser/components/aboutwelcome/content-src/aboutwelcome.scss b/browser/components/aboutwelcome/content-src/aboutwelcome.scss @@ -117,8 +117,8 @@ html { --picker-focus-ring-color: var(--in-content-item-selected); --picker-checkbox-color: var(--in-content-item-selected); --picker-checkbox-hover-color: var(--picker-checkbox-color); - --picker-backup-flair-background: var(--color-blue-0); - --picker-backup-flair-color: var(--color-blue-50); + --picker-backup-flair-background: color-mix(in srgb, var(--color-accent-primary) 20%, transparent); + --picker-backup-flair-color: var(--color-accent-primary); @media (forced-colors: active) { --picker-background-color: ButtonFace; @@ -1795,6 +1795,7 @@ html { .tiles-single-select-section { border: 0; + outline: none; display: flex; flex-wrap: wrap; gap: 5px; @@ -1820,7 +1821,7 @@ html { .select-item { position: relative; padding-top: 5px; - border: 2px solid var(--single-select-border-color); + border: 1px solid var(--single-select-border-color); border-radius: var(--border-radius-large); &:has(.input:focus), @@ -1834,7 +1835,7 @@ html { } &:has(.selected) { - border: 3px solid var(--button-background-color-primary); + border: 1px solid var(--button-background-color-primary); border-radius: var(--border-radius-large); } @@ -1877,7 +1878,6 @@ html { border-radius: 10px 10px 0 0; background-color: var(--picker-backup-flair-background); color: var(--picker-backup-flair-color); - top: -5px; &.spacer { background-color: transparent; @@ -1912,7 +1912,7 @@ html { } .tile-list-icon-wrapper { - padding-block-start: 10px; + padding-block-start: 6px; } .tile-list-text { @@ -1953,11 +1953,12 @@ html { z-index: 0; &.backup { - cursor: unset; - width: 300px; + cursor: default; + width: 225.5px; + padding-top: 0; &:has(.selected) { - border: 2px solid var(--picker-backup-flair-color); + border: 1px solid var(--color-accent-primary); } &:hover { @@ -2604,10 +2605,6 @@ html { &.slim { padding: 0; } - - &.tile-button { - cursor: pointer; - } } .secondary { @@ -2747,7 +2744,8 @@ html { .inline-image, .link-paragraph, .multi-select-container, - migration-wizard { + migration-wizard, + turn-on-scheduled-backups::part(form) { transition-delay: 0.9s; } @@ -2859,6 +2857,7 @@ html { .cta-link, .legal-paragraph, migration-wizard, + turn-on-scheduled-backups::part(form), .tile-title-container, .steps[above-button]:not(.progress-bar) { opacity: 0; @@ -2935,7 +2934,8 @@ html { .tiles-single-select-section, .inline-image, .link-paragraph, - migration-wizard { + migration-wizard, + turn-on-scheduled-backups::part(form) { opacity: 0; translate: 0 var(--translate); transition-delay: 0.2s; @@ -2991,4 +2991,12 @@ html { font-size: 0.83em; } } + + turn-on-scheduled-backups::part(form) { + transition: var(--transition); + border: 1px solid var(--border-color); + border-radius: var(--border-radius-medium); + padding: 24px 30px; + margin-block: 0 24px; + } } diff --git a/browser/components/aboutwelcome/content-src/components/CTAParagraph.jsx b/browser/components/aboutwelcome/content-src/components/CTAParagraph.jsx @@ -3,7 +3,8 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ import React from "react"; -import { Localized } from "./MSLocalized"; +import { Localized, CONFIGURABLE_STYLES } from "./MSLocalized"; +import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs"; export const CTAParagraph = props => { const { content, handleAction } = props; @@ -12,27 +13,36 @@ export const CTAParagraph = props => { return null; } + const onClick = React.useCallback( + event => { + handleAction(event); + event.preventDefault(); + }, + [handleAction] + ); + return ( - <h2 className="cta-paragraph"> + <h2 + className="cta-paragraph" + style={{ + ...AboutWelcomeUtils.getValidStyle(content?.style, CONFIGURABLE_STYLES), + }} + > <Localized text={content.text}> {content.text.string_name && typeof handleAction === "function" ? ( <span data-l10n-id={content.text.string_id} - onClick={handleAction} + onClick={onClick} onKeyUp={event => - ["Enter", " "].includes(event.key) ? handleAction(event) : null + ["Enter", " "].includes(event.key) ? onClick(event) : null } value="cta_paragraph" - tabIndex="0" role="link" > {" "} {/* <a> is valid here because of click and keyup handling. */} {/* <button> cannot be used due to fluent integration. <a> content is provided by fluent */} - {/* eslint-disable jsx-a11y/anchor-is-valid */} - <a href="" tabIndex="0" data-l10n-name={content.text.string_name}> - {" "} - </a> + <a data-l10n-name={content.text.string_name} tabIndex="0"></a> </span> ) : null} </Localized> diff --git a/browser/components/aboutwelcome/content-src/components/ContentTiles.jsx b/browser/components/aboutwelcome/content-src/components/ContentTiles.jsx @@ -9,6 +9,7 @@ import { SingleSelect } from "./SingleSelect"; import { MobileDownloads } from "./MobileDownloads"; import { MultiSelect } from "./MultiSelect"; import { EmbeddedMigrationWizard } from "./EmbeddedMigrationWizard"; +import { EmbeddedFxBackupOptIn } from "./EmbeddedFxBackupOptIn"; import { ActionChecklist } from "./ActionChecklist"; import { EmbeddedBrowser } from "./EmbeddedBrowser"; import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs"; @@ -305,6 +306,21 @@ export const ContentTiles = props => { skipButton={props.content.skip_button} /> )} + {tile.type === "fx_backup_file_path" && ( + <EmbeddedFxBackupOptIn + // TODO: This is based on user's choice on first screen + handleAction={props.handleAction} + isEncryptedBackup={content.isEncryptedBackup} + options={tile.options} + /> + )} + {tile.type === "fx_backup_password" && ( + <EmbeddedFxBackupOptIn + handleAction={props.handleAction} + isEncryptedBackup={content.isEncryptedBackup} + options={tile.options} + /> + )} </div> ) : null} </div> diff --git a/browser/components/aboutwelcome/content-src/components/EmbeddedFxBackupOptIn.jsx b/browser/components/aboutwelcome/content-src/components/EmbeddedFxBackupOptIn.jsx @@ -0,0 +1,83 @@ +/* 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 https://mozilla.org/MPL/2.0/. */ + +import React, { useEffect, useRef } from "react"; + +export const EmbeddedFxBackupOptIn = ({ + handleAction, + isEncryptedBackup, + options, +}) => { + const backupRef = useRef(null); + const { + // hide_password_input means it is the file chooser screen + hide_password_input, + hide_secondary_button, + file_path_label, + turn_on_backup_header, + create_password_label, + turn_on_backup_confirm_btn_label, + turn_on_backup_cancel_btn_label, + } = options || {}; + + useEffect(() => { + const { current } = backupRef; + + const handleBackupEvent = () => { + handleAction({ + currentTarget: { value: "tile_button" }, + action: { navigate: true }, + source: "backup_enabled", + }); + }; + const handleStateUpdate = ({ detail: { state } }) => { + if (!current || !state) { + return; + } + let { fileName, path, iconURL } = state.defaultParent; + current.setAttribute("defaultlabel", fileName); + current.setAttribute("defaultpath", path); + current.setAttribute("defaulticonurl", iconURL); + current.supportBaseLink = state.supportBaseLink; + }; + + current?.addEventListener("BackupUI:StateWasUpdated", handleStateUpdate); + current?.addEventListener( + "BackupUI:EnableScheduledBackups", + handleBackupEvent + ); + return () => { + current?.removeEventListener( + "BackupUI:EnableScheduledBackups", + handleBackupEvent + ); + current?.removeEventListener( + "BackupUI:StateWasUpdated", + handleStateUpdate + ); + }; + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + return ( + <turn-on-scheduled-backups + ref={backupRef} + hide-headers={""} + hide-password-input={ + !isEncryptedBackup || hide_password_input ? "" : undefined + } + hide-secondary-button={ + !isEncryptedBackup || hide_secondary_button ? "" : undefined + } + hide-file-path-chooser={ + isEncryptedBackup && !hide_password_input ? "" : undefined + } + embedded-fx-backup-opt-in={""} + file-path-label-l10n-id={file_path_label} + turn-on-backup-header-l10n-id={turn_on_backup_header} + create-password-label-l10n-id={create_password_label} + turn-on-backup-confirm-btn-l10n-id={turn_on_backup_confirm_btn_label} + turn-on-backup-cancel-btn-l10n-id={turn_on_backup_cancel_btn_label} + ></turn-on-scheduled-backups> + ); +}; diff --git a/browser/components/aboutwelcome/content-src/components/MSLocalized.jsx b/browser/components/aboutwelcome/content-src/components/MSLocalized.jsx @@ -25,6 +25,15 @@ export const CONFIGURABLE_STYLES = [ "width", "borderBlockStart", "borderBlockEnd", + "top", + "bottom", + "left", + "right", + "inset", + "insetBlock", + "insetInline", + "minHeight", + "minWidth", ]; const ZAP_SIZE_THRESHOLD = 160; diff --git a/browser/components/aboutwelcome/content-src/components/MultiStageProtonScreen.jsx b/browser/components/aboutwelcome/content-src/components/MultiStageProtonScreen.jsx @@ -245,15 +245,7 @@ export class ProtonScreen extends React.PureComponent { this.mainContentHeader.focus(); } - getScreenClassName( - isFirstScreen, - isLastScreen, - includeNoodles, - isVideoOnboarding, - isAddonsPicker - ) { - const screenClass = `screen-${this.props.order % 2 !== 0 ? 1 : 2}`; - + getScreenClassName(includeNoodles, isVideoOnboarding, isAddonsPicker) { if (isVideoOnboarding) { return "with-video"; } @@ -262,9 +254,14 @@ export class ProtonScreen extends React.PureComponent { return "addons-picker"; } - return `${isFirstScreen ? `dialog-initial` : ``} ${ - isLastScreen ? `dialog-last` : `` - } ${includeNoodles ? `with-noodles` : ``} ${screenClass}`; + const screenClass = `screen-${this.props.order % 2 !== 0 ? 1 : 2}`; + const dialogInitial = + this.props.isFirstScreen && this.props.previousOrder < 0 + ? `dialog-initial` + : ``; + const dialogLast = this.props.isLastScreen ? `dialog-last` : ``; + + return `${screenClass} ${dialogInitial} ${dialogLast} ${includeNoodles ? `with-noodles` : ``}`; } renderTitle({ title, title_logo }) { @@ -590,8 +587,6 @@ export class ProtonScreen extends React.PureComponent { content, isRtamo, addonType, - isFirstScreen, - isLastScreen, isSingleScreen, forceHideStepsIndicator, ariaRole, @@ -613,8 +608,6 @@ export class ProtonScreen extends React.PureComponent { // by checking if screen order is even or odd. const screenClassName = isCenterPosition ? this.getScreenClassName( - isFirstScreen, - isLastScreen, includeNoodles, content?.video_container, content.tiles?.type === "addons-picker" diff --git a/browser/components/aboutwelcome/content-src/components/SingleSelect.jsx b/browser/components/aboutwelcome/content-src/components/SingleSelect.jsx @@ -146,7 +146,6 @@ export const SingleSelect = ({ {/* eslint-disable jsx-a11y/no-noninteractive-element-interactions */} <label className={`select-item ${type}`} - title={value} onKeyDown={e => handleKeyDown(e)} style={{ ...AboutWelcomeUtils.getValidStyle( diff --git a/browser/components/aboutwelcome/content-src/components/TileButton.jsx b/browser/components/aboutwelcome/content-src/components/TileButton.jsx @@ -3,7 +3,6 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ import React, { useRef } from "react"; -import { AboutWelcomeUtils } from "../lib/aboutwelcome-utils.mjs"; import { Localized } from "./MSLocalized"; export const TileButton = props => { @@ -14,23 +13,6 @@ export const TileButton = props => { return null; } - const CONFIGURABLE_STYLES = [ - "background", - "borderRadius", - "height", - "marginBlock", - "marginBlockStart", - "marginBlockEnd", - "marginInline", - "paddingBlock", - "paddingBlockStart", - "paddingBlockEnd", - "paddingInline", - "paddingInlineStart", - "paddingInlineEnd", - "width", - ]; - function onClick(event) { let mockEvent = { currentTarget: ref.current, @@ -49,7 +31,6 @@ export const TileButton = props => { value="tile_button" ref={ref} className={`${content.style} tile-button slim`} - style={AboutWelcomeUtils.getValidStyle(content, CONFIGURABLE_STYLES)} /> </Localized> ); diff --git a/browser/components/aboutwelcome/content/aboutwelcome.bundle.js b/browser/components/aboutwelcome/content/aboutwelcome.bundle.js @@ -151,7 +151,7 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _MultiStageProtonScreen__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6); /* harmony import */ var _LanguageSwitcher__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(7); /* harmony import */ var _SubmenuButton__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(12); -/* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(26); +/* harmony import */ var _lib_addUtmParams_mjs__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(27); /* 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/. */ @@ -909,7 +909,7 @@ __webpack_require__.r(__webpack_exports__); * You can obtain one at http://mozilla.org/MPL/2.0/. */ -const CONFIGURABLE_STYLES = ["color", "display", "fontSize", "fontWeight", "letterSpacing", "lineHeight", "marginBlock", "marginBlockStart", "marginBlockEnd", "marginInline", "paddingBlock", "paddingBlockStart", "paddingBlockEnd", "paddingInline", "paddingInlineStart", "paddingInlineEnd", "textAlign", "whiteSpace", "width", "borderBlockStart", "borderBlockEnd"]; +const CONFIGURABLE_STYLES = ["color", "display", "fontSize", "fontWeight", "letterSpacing", "lineHeight", "marginBlock", "marginBlockStart", "marginBlockEnd", "marginInline", "paddingBlock", "paddingBlockStart", "paddingBlockEnd", "paddingInline", "paddingInlineStart", "paddingInlineEnd", "textAlign", "whiteSpace", "width", "borderBlockStart", "borderBlockEnd", "top", "bottom", "left", "right", "inset", "insetBlock", "insetInline", "minHeight", "minWidth"]; const ZAP_SIZE_THRESHOLD = 160; /** @@ -1225,15 +1225,17 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom } this.mainContentHeader.focus(); } - getScreenClassName(isFirstScreen, isLastScreen, includeNoodles, isVideoOnboarding, isAddonsPicker) { - const screenClass = `screen-${this.props.order % 2 !== 0 ? 1 : 2}`; + getScreenClassName(includeNoodles, isVideoOnboarding, isAddonsPicker) { if (isVideoOnboarding) { return "with-video"; } if (isAddonsPicker) { return "addons-picker"; } - return `${isFirstScreen ? `dialog-initial` : ``} ${isLastScreen ? `dialog-last` : ``} ${includeNoodles ? `with-noodles` : ``} ${screenClass}`; + const screenClass = `screen-${this.props.order % 2 !== 0 ? 1 : 2}`; + const dialogInitial = this.props.isFirstScreen && this.props.previousOrder < 0 ? `dialog-initial` : ``; + const dialogLast = this.props.isLastScreen ? `dialog-last` : ``; + return `${screenClass} ${dialogInitial} ${dialogLast} ${includeNoodles ? `with-noodles` : ``}`; } renderTitle({ title, @@ -1486,8 +1488,6 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom content, isRtamo, addonType, - isFirstScreen, - isLastScreen, isSingleScreen, forceHideStepsIndicator, ariaRole, @@ -1501,7 +1501,7 @@ class ProtonScreen extends (react__WEBPACK_IMPORTED_MODULE_0___default().PureCom const textColorClass = content.text_color ? `${content.text_color}-text` : ""; // Assign proton screen style 'screen-1' or 'screen-2' to centered screens // by checking if screen order is even or odd. - const screenClassName = isCenterPosition ? this.getScreenClassName(isFirstScreen, isLastScreen, includeNoodles, content?.video_container, content.tiles?.type === "addons-picker") : ""; + const screenClassName = isCenterPosition ? this.getScreenClassName(includeNoodles, content?.video_container, content.tiles?.type === "addons-picker") : ""; const isEmbeddedMigration = content.tiles?.type === "migration-wizard"; const isSystemPromptStyleSpotlight = content.isSystemPromptStyleSpotlight === true; const combinedStyles = this.getCombinedInnerStyles(content, isWideScreen); @@ -1857,12 +1857,14 @@ __webpack_require__.r(__webpack_exports__); /* 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__(5); +/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(3); /* 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 CTAParagraph = props => { const { content, @@ -1871,22 +1873,27 @@ const CTAParagraph = props => { if (!content?.text) { return null; } + const onClick = react__WEBPACK_IMPORTED_MODULE_0___default().useCallback(event => { + handleAction(event); + event.preventDefault(); + }, [handleAction]); return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("h2", { - className: "cta-paragraph" + className: "cta-paragraph", + style: { + ..._lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_2__.AboutWelcomeUtils.getValidStyle(content?.style, _MSLocalized__WEBPACK_IMPORTED_MODULE_1__.CONFIGURABLE_STYLES) + } }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: content.text }, content.text.string_name && typeof handleAction === "function" ? /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("span", { "data-l10n-id": content.text.string_id, - onClick: handleAction, - onKeyUp: event => ["Enter", " "].includes(event.key) ? handleAction(event) : null, + onClick: onClick, + onKeyUp: event => ["Enter", " "].includes(event.key) ? onClick(event) : null, value: "cta_paragraph", - tabIndex: "0", role: "link" }, " ", /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("a", { - href: "", - tabIndex: "0", - "data-l10n-name": content.text.string_name - }, " ")) : null)); + "data-l10n-name": content.text.string_name, + tabIndex: "0" + })) : null)); }; /***/ }), @@ -2255,10 +2262,11 @@ __webpack_require__.r(__webpack_exports__); /* harmony import */ var _MobileDownloads__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(20); /* harmony import */ var _MultiSelect__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(21); /* harmony import */ var _EmbeddedMigrationWizard__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(22); -/* harmony import */ var _ActionChecklist__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(23); -/* harmony import */ var _EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(24); -/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(3); -/* harmony import */ var _EmbeddedBackupRestore__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(25); +/* harmony import */ var _EmbeddedFxBackupOptIn__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(23); +/* harmony import */ var _ActionChecklist__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(24); +/* harmony import */ var _EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_9__ = __webpack_require__(25); +/* harmony import */ var _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_10__ = __webpack_require__(3); +/* harmony import */ var _EmbeddedBackupRestore__WEBPACK_IMPORTED_MODULE_11__ = __webpack_require__(26); /* 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/. */ @@ -2274,6 +2282,7 @@ __webpack_require__.r(__webpack_exports__); + const HEADER_STYLES = ["backgroundColor", "border", "padding", "margin", "width", "height"]; const TILE_STYLES = ["marginBlock", "marginInline", "paddingBlock", "paddingInline"]; const CONTAINER_STYLES = ["padding", "margin", "marginBlock", "marginInline", "paddingBlock", "paddingInline", "flexDirection", "flexWrap", "flexFlow", "flexGrow", "flexShrink", "justifyContent", "alignItems", "gap"]; @@ -2392,11 +2401,11 @@ const ContentTiles = props => { const toggleTile = (index, tile) => { const tileId = `${tile.type}${tile.id ? "_" : ""}${tile.id ?? ""}_header`; setExpandedTileIndex(prevIndex => prevIndex === index ? null : index); - _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, tileId); + _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_10__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, tileId); }; const toggleTiles = () => { setTilesHeaderExpanded(prev => !prev); - _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "content_tiles_header"); + _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_10__.AboutWelcomeUtils.sendActionTelemetry(props.messageId, "content_tiles_header"); }; function getTileMultiSelects(screenMultiSelects, index) { return screenMultiSelects?.[`tile-${index}`]; @@ -2414,13 +2423,13 @@ const ContentTiles = props => { return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { key: index, className: `content-tile ${header ? "has-header" : ""}`, - style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__.AboutWelcomeUtils.getTileStyle(tile, TILE_STYLES) + style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_10__.AboutWelcomeUtils.getTileStyle(tile, TILE_STYLES) }, header?.title && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { className: "tile-header secondary", onClick: () => toggleTile(index, tile), "aria-expanded": isExpanded, "aria-controls": `tile-content-${index}`, - style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__.AboutWelcomeUtils.getValidStyle(header.style, HEADER_STYLES) + style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_10__.AboutWelcomeUtils.getValidStyle(header.style, HEADER_STYLES) }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { className: "header-text-container" }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { @@ -2482,25 +2491,35 @@ const ContentTiles = props => { content: { tiles: tile } - }), tile.type === "action_checklist" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ActionChecklist__WEBPACK_IMPORTED_MODULE_7__.ActionChecklist, { + }), tile.type === "action_checklist" && tile.data && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_ActionChecklist__WEBPACK_IMPORTED_MODULE_8__.ActionChecklist, { content: content, message_id: props.messageId - }), tile.type === "embedded_browser" && tile.data?.url && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_8__.EmbeddedBrowser, { + }), tile.type === "embedded_browser" && tile.data?.url && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedBrowser__WEBPACK_IMPORTED_MODULE_9__.EmbeddedBrowser, { url: tile.data.url, style: tile.data.style - }), tile.type === "backup_restore" && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedBackupRestore__WEBPACK_IMPORTED_MODULE_10__.EmbeddedBackupRestore, { + }), tile.type === "backup_restore" && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedBackupRestore__WEBPACK_IMPORTED_MODULE_11__.EmbeddedBackupRestore, { handleAction: props.handleAction, content: { tiles: tile }, skipButton: props.content.skip_button + }), tile.type === "fx_backup_file_path" && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedFxBackupOptIn__WEBPACK_IMPORTED_MODULE_7__.EmbeddedFxBackupOptIn + // TODO: This is based on user's choice on first screen + , { + handleAction: props.handleAction, + isEncryptedBackup: content.isEncryptedBackup, + options: tile.options + }), tile.type === "fx_backup_password" && /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_EmbeddedFxBackupOptIn__WEBPACK_IMPORTED_MODULE_7__.EmbeddedFxBackupOptIn, { + handleAction: props.handleAction, + isEncryptedBackup: content.isEncryptedBackup, + options: tile.options })) : null); }; const renderContentTiles = () => { if (Array.isArray(tiles)) { return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("div", { id: "content-tiles-container", - style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_9__.AboutWelcomeUtils.getValidStyle(content?.contentTilesContainer?.style, CONTAINER_STYLES) + style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_10__.AboutWelcomeUtils.getValidStyle(content?.contentTilesContainer?.style, CONTAINER_STYLES) }, tiles.map((tile, index) => renderContentTile(tile, index))); } // If tiles is not an array render the tile alone without a container @@ -2872,7 +2891,6 @@ const SingleSelect = ({ text: valOrObj(tooltip) }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("label", { className: `select-item ${type}`, - title: value, onKeyDown: e => handleKeyDown(e), style: { ..._lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_4__.AboutWelcomeUtils.getValidStyle(style, CONFIGURABLE_STYLES), @@ -2925,15 +2943,13 @@ __webpack_require__.r(__webpack_exports__); /* harmony export */ }); /* 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 _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3); -/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(5); +/* harmony import */ var _MSLocalized__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(5); /* 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 TileButton = props => { const { content, @@ -2944,7 +2960,6 @@ const TileButton = props => { if (!content) { return null; } - const CONFIGURABLE_STYLES = ["background", "borderRadius", "height", "marginBlock", "marginBlockStart", "marginBlockEnd", "marginInline", "paddingBlock", "paddingBlockStart", "paddingBlockEnd", "paddingInline", "paddingInlineStart", "paddingInlineEnd", "width"]; function onClick(event) { let mockEvent = { currentTarget: ref.current, @@ -2954,15 +2969,14 @@ const TileButton = props => { }; handleAction(mockEvent); } - return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_2__.Localized, { + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement(_MSLocalized__WEBPACK_IMPORTED_MODULE_1__.Localized, { text: content.label }, /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("button", { id: `tile-button-${inputName}`, onClick: onClick, value: "tile_button", ref: ref, - className: `${content.style} tile-button slim`, - style: _lib_aboutwelcome_utils_mjs__WEBPACK_IMPORTED_MODULE_1__.AboutWelcomeUtils.getValidStyle(content, CONFIGURABLE_STYLES) + className: `${content.style} tile-button slim` })); }; @@ -3343,6 +3357,93 @@ const EmbeddedMigrationWizard = ({ __webpack_require__.r(__webpack_exports__); /* harmony export */ __webpack_require__.d(__webpack_exports__, { +/* harmony export */ EmbeddedFxBackupOptIn: () => (/* binding */ EmbeddedFxBackupOptIn) +/* harmony export */ }); +/* 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__); +/* 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 https://mozilla.org/MPL/2.0/. */ + + +const EmbeddedFxBackupOptIn = ({ + handleAction, + isEncryptedBackup, + options +}) => { + const backupRef = (0,react__WEBPACK_IMPORTED_MODULE_0__.useRef)(null); + const { + // hide_password_input means it is the file chooser screen + hide_password_input, + hide_secondary_button, + file_path_label, + turn_on_backup_header, + create_password_label, + turn_on_backup_confirm_btn_label, + turn_on_backup_cancel_btn_label + } = options || {}; + (0,react__WEBPACK_IMPORTED_MODULE_0__.useEffect)(() => { + const { + current + } = backupRef; + const handleBackupEvent = () => { + handleAction({ + currentTarget: { + value: "tile_button" + }, + action: { + navigate: true + }, + source: "backup_enabled" + }); + }; + const handleStateUpdate = ({ + detail: { + state + } + }) => { + if (!current || !state) { + return; + } + let { + fileName, + path, + iconURL + } = state.defaultParent; + current.setAttribute("defaultlabel", fileName); + current.setAttribute("defaultpath", path); + current.setAttribute("defaulticonurl", iconURL); + current.supportBaseLink = state.supportBaseLink; + }; + current?.addEventListener("BackupUI:StateWasUpdated", handleStateUpdate); + current?.addEventListener("BackupUI:EnableScheduledBackups", handleBackupEvent); + return () => { + current?.removeEventListener("BackupUI:EnableScheduledBackups", handleBackupEvent); + current?.removeEventListener("BackupUI:StateWasUpdated", handleStateUpdate); + }; + }, []); // eslint-disable-line react-hooks/exhaustive-deps + + return /*#__PURE__*/react__WEBPACK_IMPORTED_MODULE_0___default().createElement("turn-on-scheduled-backups", { + ref: backupRef, + "hide-headers": "", + "hide-password-input": !isEncryptedBackup || hide_password_input ? "" : undefined, + "hide-secondary-button": !isEncryptedBackup || hide_secondary_button ? "" : undefined, + "hide-file-path-chooser": isEncryptedBackup && !hide_password_input ? "" : undefined, + "embedded-fx-backup-opt-in": "", + "file-path-label-l10n-id": file_path_label, + "turn-on-backup-header-l10n-id": turn_on_backup_header, + "create-password-label-l10n-id": create_password_label, + "turn-on-backup-confirm-btn-l10n-id": turn_on_backup_confirm_btn_label, + "turn-on-backup-cancel-btn-l10n-id": turn_on_backup_cancel_btn_label + }); +}; + +/***/ }), +/* 24 */ +/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { + +__webpack_require__.r(__webpack_exports__); +/* harmony export */ __webpack_require__.d(__webpack_exports__, { /* harmony export */ ActionChecklist: () => (/* binding */ ActionChecklist), /* harmony export */ ActionChecklistItem: () => (/* binding */ ActionChecklistItem), /* harmony export */ ActionChecklistProgressBar: () => (/* binding */ ActionChecklistProgressBar) @@ -3496,7 +3597,7 @@ const ActionChecklist = ({ }; /***/ }), -/* 24 */ +/* 25 */ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); @@ -3564,7 +3665,7 @@ const EmbeddedBrowserInner = ({ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (EmbeddedBrowser); /***/ }), -/* 25 */ +/* 26 */ /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); @@ -3641,7 +3742,7 @@ const EmbeddedBackupRestore = ({ }; /***/ }), -/* 26 */ +/* 27 */ /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __webpack_require__) => { __webpack_require__.r(__webpack_exports__); diff --git a/browser/components/aboutwelcome/content/aboutwelcome.css b/browser/components/aboutwelcome/content/aboutwelcome.css @@ -1062,8 +1062,8 @@ html { --picker-focus-ring-color: var(--in-content-item-selected); --picker-checkbox-color: var(--in-content-item-selected); --picker-checkbox-hover-color: var(--picker-checkbox-color); - --picker-backup-flair-background: var(--color-blue-0); - --picker-backup-flair-color: var(--color-blue-50); + --picker-backup-flair-background: color-mix(in srgb, var(--color-accent-primary) 20%, transparent); + --picker-backup-flair-color: var(--color-accent-primary); font-family: system-ui; font-size: 16px; position: relative; @@ -2685,6 +2685,7 @@ html { } .onboardingContainer .tiles-single-select-section { border: 0; + outline: none; display: flex; flex-wrap: wrap; gap: 5px; @@ -2706,7 +2707,7 @@ html { .onboardingContainer .tiles-single-select-section.single-select .select-item, .onboardingContainer .tiles-single-select-section.theme .select-item { position: relative; padding-top: 5px; - border: 2px solid var(--single-select-border-color); + border: 1px solid var(--single-select-border-color); border-radius: var(--border-radius-large); } .onboardingContainer .tiles-single-select-section.single-select .select-item:has(.input:focus), .onboardingContainer .tiles-single-select-section.single-select .select-item:has(.selected), .onboardingContainer .tiles-single-select-section.theme .select-item:has(.input:focus), .onboardingContainer .tiles-single-select-section.theme .select-item:has(.selected) { @@ -2717,7 +2718,7 @@ html { outline-offset: 5px; } .onboardingContainer .tiles-single-select-section.single-select .select-item:has(.selected), .onboardingContainer .tiles-single-select-section.theme .select-item:has(.selected) { - border: 3px solid var(--button-background-color-primary); + border: 1px solid var(--button-background-color-primary); border-radius: var(--border-radius-large); } .onboardingContainer .tiles-single-select-section.single-select .select-item:hover, .onboardingContainer .tiles-single-select-section.theme .select-item:hover { @@ -2756,7 +2757,6 @@ html { border-radius: 10px 10px 0 0; background-color: var(--picker-backup-flair-background); color: var(--picker-backup-flair-color); - top: -5px; } .onboardingContainer .tiles-single-select-section.single-select .select-item .flair.centered.backup.spacer, .onboardingContainer .tiles-single-select-section.theme .select-item .flair.centered.backup.spacer { background-color: transparent; @@ -2782,7 +2782,7 @@ html { margin-block-end: 8px; } .onboardingContainer .tiles-single-select-section.single-select .select-item .tile-list-container .tile-list-icon-wrapper, .onboardingContainer .tiles-single-select-section.theme .select-item .tile-list-container .tile-list-icon-wrapper { - padding-block-start: 10px; + padding-block-start: 6px; } .onboardingContainer .tiles-single-select-section.single-select .select-item .tile-list-container .tile-list-text, .onboardingContainer .tiles-single-select-section.theme .select-item .tile-list-container .tile-list-text { margin-inline-start: 8px; @@ -2812,11 +2812,12 @@ html { z-index: 0; } .onboardingContainer .tiles-single-select-section .select-item.backup { - cursor: unset; - width: 300px; + cursor: default; + width: 225.5px; + padding-top: 0; } .onboardingContainer .tiles-single-select-section .select-item.backup:has(.selected) { - border: 2px solid var(--picker-backup-flair-color); + border: 1px solid var(--color-accent-primary); } .onboardingContainer .tiles-single-select-section .select-item.backup:hover { background-color: transparent; @@ -3367,12 +3368,6 @@ html { .onboardingContainer .submenu-button.slim { padding: 0; } -.onboardingContainer .primary.tile-button, -.onboardingContainer .secondary.tile-button, -.onboardingContainer .additional-cta.tile-button, -.onboardingContainer .submenu-button.tile-button { - cursor: pointer; -} .onboardingContainer .secondary { background-color: var(--button-background-color); color: var(--button-text-color); @@ -3491,7 +3486,8 @@ html { .onboardingContainer .dialog-initial .inline-image, .onboardingContainer .dialog-initial .link-paragraph, .onboardingContainer .dialog-initial .multi-select-container, -.onboardingContainer .dialog-initial migration-wizard { +.onboardingContainer .dialog-initial migration-wizard, +.onboardingContainer .dialog-initial turn-on-scheduled-backups::part(form) { transition-delay: 0.9s; } .onboardingContainer .dialog-initial .primary, @@ -3578,6 +3574,7 @@ html { .onboardingContainer.transition-in .screen .cta-link, .onboardingContainer.transition-in .screen .legal-paragraph, .onboardingContainer.transition-in .screen migration-wizard, +.onboardingContainer.transition-in .screen turn-on-scheduled-backups::part(form), .onboardingContainer.transition-in .screen .tile-title-container, .onboardingContainer.transition-in .screen .steps[above-button]:not(.progress-bar) { opacity: 0; @@ -3632,7 +3629,8 @@ html { .onboardingContainer.transition-out .screen:not(.dialog-last) .tiles-single-select-section, .onboardingContainer.transition-out .screen:not(.dialog-last) .inline-image, .onboardingContainer.transition-out .screen:not(.dialog-last) .link-paragraph, -.onboardingContainer.transition-out .screen:not(.dialog-last) migration-wizard { +.onboardingContainer.transition-out .screen:not(.dialog-last) migration-wizard, +.onboardingContainer.transition-out .screen:not(.dialog-last) turn-on-scheduled-backups::part(form) { opacity: 0; translate: 0 var(--translate); transition-delay: 0.2s; @@ -3676,3 +3674,10 @@ html { .onboardingContainer migration-wizard::part(deck) { font-size: 0.83em; } +.onboardingContainer turn-on-scheduled-backups::part(form) { + transition: var(--transition); + border: 1px solid var(--border-color); + border-radius: var(--border-radius-medium); + padding: 24px 30px; + margin-block: 0 24px; +} diff --git a/browser/components/aboutwelcome/content/onboarding.ftl b/browser/components/aboutwelcome/content/onboarding.ftl @@ -16,3 +16,34 @@ amo-screen-primary-cta = Explore staff-recommended add-ons browser-aboutwelcome-button = .label = Finish setup .tooltiptext = Finish setting up { -brand-short-name }. + +## Fx Backup onboarding: Create Backup spotlight + +fx-backup-opt-in-header = Choose file location +fx-backup-opt-in-filepath-label = Pick a location you plan to transfer to a new device, like OneDrive. +fx-backup-opt-in-create-password-label = Enter password +fx-backup-opt-in-confirm-btn-label = Continue +fx-backup-opt-in-cancel-btn-label = Back + +create-backup-screen-1-title = Upgrading to Windows 11? + Let’s back up your { -brand-product-name } data. +create-backup-screen-1-subtitle = Automatically protect your passwords, bookmarks, and more in 1-2 minutes. +create-backup-screen-1-flair = Recommended +create-backup-learn-more-link = <a data-l10n-name="learn-more-label">Learn more</a> +create-backup-screen-1-sync-label = Sync with { -brand-product-name } +create-backup-screen-1-sync-body = Back up on all signed in devices +create-backup-screen-1-backup-label = Back up to PC +create-backup-screen-1-backup-body = Save to this device +create-backup-select-tile-button-label = Select +create-backup-back-button-label = Back +create-backup-show-fewer = + .label = Show fewer like this +create-backup-screen-2-title = Choose { -brand-product-name } data to back up +create-backup-screen-2-subtitle = Only takes a minute. Your data is backed up once a day. +create-backup-screen-2-easy-label = Easy setup +create-backup-screen-2-easy-list-1 = Bookmarks, history, settings, and more +create-backup-screen-2-easy-list-2 = Doesn’t include passwords and payments +create-backup-screen-2-easy-list-3 = Not encrypted +create-backup-screen-2-all-label = All data +create-backup-screen-2-all-list-2 = Includes passwords and payments +create-backup-screen-2-all-list-3 = Encrypted with a password diff --git a/browser/components/aboutwelcome/karma.mc.config.js b/browser/components/aboutwelcome/karma.mc.config.js @@ -129,6 +129,12 @@ module.exports = function (config) { functions: 0, branches: 0, }, + "content-src/components/EmbeddedFxBackupOptIn.jsx": { + statements: 60, + lines: 60, + functions: 60, + branches: 60, + }, "content-src/components/MSLocalized.jsx": { statements: 77.42, lines: 77.42, diff --git a/browser/components/aboutwelcome/tests/unit/CTAParagraph.test.jsx b/browser/components/aboutwelcome/tests/unit/CTAParagraph.test.jsx @@ -38,7 +38,7 @@ describe("CTAParagraph component", () => { it("should call handleAction method when button is link is clicked", () => { const btnLink = wrapper.find(".cta-paragraph span"); - btnLink.simulate("click"); + btnLink.simulate("click", { preventDefault() {} }); assert.calledOnce(handleAction); }); diff --git a/browser/components/aboutwelcome/tests/unit/EmbeddedFxBackupOptIn.test.jsx b/browser/components/aboutwelcome/tests/unit/EmbeddedFxBackupOptIn.test.jsx @@ -0,0 +1,83 @@ +import React from "react"; +import { mount } from "enzyme"; +import { EmbeddedFxBackupOptIn } from "content-src/components/EmbeddedFxBackupOptIn.jsx"; + +function getFxBackupComponent(wrapper) { + return wrapper.find("turn-on-scheduled-backups").getDOMNode(); +} + +describe("EmbeddedFxBackupOptIn", () => { + it("does not crash if ref is null", () => { + const wrapper = mount(<EmbeddedFxBackupOptIn isEncryptedBackup={false} />); + wrapper.unmount(); + }); + + it("unencrypted backups always show file chooser screen", () => { + const wrapper = mount( + <EmbeddedFxBackupOptIn + isEncryptedBackup={false} + options={{ hide_password_input: false }} + /> + ); + const el = getFxBackupComponent(wrapper); + + assert.strictEqual(el.hasAttribute("hide-password-input"), true); + assert.strictEqual(el.hasAttribute("hide-file-path-chooser"), false); + + wrapper.unmount(); + }); + + it("encrypted backups show file chooser first if hide_password_input=true", () => { + const wrapper = mount( + <EmbeddedFxBackupOptIn + isEncryptedBackup={true} + options={{ hide_password_input: true }} + /> + ); + const el = getFxBackupComponent(wrapper); + + assert.strictEqual(el.hasAttribute("hide-password-input"), true); + assert.strictEqual(el.hasAttribute("hide-file-path-chooser"), false); + + wrapper.unmount(); + }); + + it("encrypted backups show password input screen if hide_password_input=false", () => { + const wrapper = mount( + <EmbeddedFxBackupOptIn + isEncryptedBackup={true} + options={{ hide_password_input: false }} + /> + ); + const el = getFxBackupComponent(wrapper); + + assert.strictEqual(el.hasAttribute("hide-password-input"), false); + assert.strictEqual(el.hasAttribute("hide-file-path-chooser"), true); + + wrapper.unmount(); + }); + + it("updates screen when hide_password_input changes for encrypted backups", () => { + const wrapper = mount( + <EmbeddedFxBackupOptIn + isEncryptedBackup={true} + options={{ hide_password_input: true }} + /> + ); + let el = getFxBackupComponent(wrapper); + + // Show file chooser screen first + assert.strictEqual(el.hasAttribute("hide-password-input"), true); + assert.strictEqual(el.hasAttribute("hide-file-path-chooser"), false); + + // Switch to password input screen + wrapper.setProps({ options: { hide_password_input: false } }); + wrapper.update(); + el = getFxBackupComponent(wrapper); + + assert.strictEqual(el.hasAttribute("hide-password-input"), false); + assert.strictEqual(el.hasAttribute("hide-file-path-chooser"), true); + + wrapper.unmount(); + }); +}); diff --git a/browser/components/asrouter/modules/PanelTestProvider.sys.mjs b/browser/components/asrouter/modules/PanelTestProvider.sys.mjs @@ -32,6 +32,7 @@ const MESSAGES = () => [ lifetime: 100, }, content: { + id: "TEST_BACKUP_SPOTLIGHT", template: "multistage", modal: "tab", screens: [ @@ -41,17 +42,40 @@ const MESSAGES = () => [ content: { position: "center", screen_style: { - width: "700px", - }, - logo: { - height: "0px", - imageURL: "", + width: "579px", }, + split_content_padding_block: "32px", title: { - raw: "Upgrading to Windows 11? Let's back up your Firefox data", + string_id: "create-backup-screen-1-title", + letterSpacing: "revert", + whiteSpace: "preserve-breaks", + lineHeight: "28px", + marginBlock: "0", }, subtitle: { - raw: "Automatically protect your passwords, bookmarks, and more in 1-2 minutes. Learn more", + string_id: "create-backup-screen-1-subtitle", + fontSize: "0.8125em", + letterSpacing: "revert", + marginBlock: "12px 0", + }, + cta_paragraph: { + text: { + string_id: "create-backup-learn-more-link", + string_name: "learn-more-label", + fontSize: "0.8125em", + }, + style: { + marginBlock: "0", + lineHeight: "100%", + letterSpacing: "revert", + }, + action: { + type: "OPEN_URL", + data: { + args: "https://support.mozilla.org/1/firefox/%VERSION%/%OS%/%LOCALE%/firefox-backup", + where: "tabshifted", + }, + }, }, tiles: { type: "single-select", @@ -67,43 +91,60 @@ const MESSAGES = () => [ icon: { background: "center / contain no-repeat url('chrome://browser/content/asrouter/assets/fox-with-box-on-cloud.svg')", - width: "60px", - height: "42px", - marginBlockStart: "10px", - paddingBlockEnd: "10px", + width: "133.9601px", + height: "90.1186px", + marginBlockStart: "8px", borderRadius: "5px", }, id: "sync", flair: { centered: true, text: { - raw: "Recommended", + string_id: "create-backup-screen-1-flair", + fontSize: "0.625em", + fontWeight: "600", + top: "revert", + lineHeight: "normal", }, }, label: { - raw: "Sync to cloud", + string_id: "create-backup-screen-1-sync-label", fontSize: 17, fontWeight: 600, }, body: { - raw: "Back up on all signed in devices", - fontWeight: 300, + string_id: "create-backup-screen-1-sync-body", + fontSize: "0.625em", + fontWeight: "400", marginBlock: "-6px 16px", + color: "var(--text-color-deemphasized)", }, tilebutton: { label: { - raw: "Select", + string_id: "create-backup-select-tile-button-label", + minHeight: "24px", + minWidth: "revert", + lineHeight: "100%", + paddingBlock: "4px", + paddingInline: "16px", + marginBlock: "0 16px", }, style: "primary", - marginBlock: "0 16px", action: { type: "FXA_SIGNIN_FLOW", dismiss: "actionResult", needsAwait: true, data: { - entrypoint: "onboarding", + autoClose: false, + entrypoint: "create-backup-spotlight", extraParams: { - utm_content: "migration-onboarding", + service: "sync", + entrypoint_experiment: "fx-backup-onboarding", + entrypoint_variation: "1", + utm_medium: "firefox-desktop", + utm_source: "spotlight", + utm_campaign: "fx-backup-onboarding", + utm_term: "fx-backup-onboarding-spotlight-1", }, }, }, @@ -115,10 +156,9 @@ const MESSAGES = () => [ icon: { background: "center / contain no-repeat url('chrome://browser/content/asrouter/assets/fox-with-locked-box.svg')", - width: "60px", - height: "40px", - marginBlockStart: "10px", - paddingBlockEnd: "10px", + width: "114.475px", + height: "90.1186px", + marginBlockStart: "8px", borderRadius: "5px", }, id: "backup", @@ -127,21 +167,28 @@ const MESSAGES = () => [ spacer: true, }, label: { - raw: "Back up to PC", + string_id: "create-backup-screen-1-backup-label", fontSize: 17, fontWeight: 600, }, body: { - raw: "Save to this device", - fontWeight: 300, + string_id: "create-backup-screen-1-backup-body", + fontSize: "0.625em", + fontWeight: "400", marginBlock: "-6px 16px", + color: "var(--text-color-deemphasized)", }, tilebutton: { label: { - raw: "Select", + string_id: "create-backup-select-tile-button-label", + minHeight: "24px", + minWidth: "revert", + lineHeight: "100%", + paddingBlock: "4px", + paddingInline: "16px", + marginBlock: "0 16px", }, style: "secondary", - marginBlock: "0 16px", action: { navigate: true, }, @@ -152,6 +199,12 @@ const MESSAGES = () => [ additional_button: { label: { string_id: "fx-view-discoverability-secondary-button-label", + fontSize: "0.75em", + minHeight: "24px", + minWidth: "revert", + lineHeight: "100%", + paddingBlock: "4px", + paddingInline: "12px", }, style: "secondary", flow: "row", @@ -164,26 +217,16 @@ const MESSAGES = () => [ }, }, submenu_button: { + label: { + minHeight: "24px", + minWidth: "24px", + paddingBlock: "0", + paddingInline: "0", + }, submenu: [ { type: "action", - label: { - string_id: "split-dismiss-button-dont-show-option", - }, - action: { - type: "BLOCK_MESSAGE", - data: { - id: "TEST_BACKUP_SPOTLIGHT", - }, - dismiss: true, - }, - id: "block_recommendation", - }, - { - type: "action", - label: { - string_id: "split-dismiss-button-show-fewer-option", - }, + label: { string_id: "create-backup-show-fewer" }, action: { type: "MULTI_ACTION", dismiss: true, @@ -193,7 +236,7 @@ const MESSAGES = () => [ type: "SET_PREF", data: { pref: { - name: "messaging-system-action.firefox-view-recommendations", + name: "messaging-system-action.show-fewer-backup-messages", value: true, }, }, @@ -201,7 +244,7 @@ const MESSAGES = () => [ { type: "BLOCK_MESSAGE", data: { - id: "TASKBAR_TAB_TEST_CALLOUT", + id: "TEST_BACKUP_SPOTLIGHT", }, }, ], @@ -209,24 +252,6 @@ const MESSAGES = () => [ }, id: "show_fewer_recommendations", }, - { - type: "separator", - }, - { - type: "action", - label: { - string_id: "split-dismiss-button-manage-settings-option", - }, - action: { - type: "OPEN_ABOUT_PAGE", - data: { - args: "preferences#general-cfrfeatures", - where: "tab", - }, - dismiss: true, - }, - id: "manage_settings", - }, ], attached_to: "additional_button", }, @@ -238,17 +263,20 @@ const MESSAGES = () => [ content: { position: "center", screen_style: { - width: "700px", - }, - logo: { - height: "0px", - imageURL: "", + width: "579px", }, + split_content_padding_block: "32px", title: { - raw: "Choose Firefox data to back up", + string_id: "create-backup-screen-2-title", + letterSpacing: "revert", + lineHeight: "28px", + marginBlock: "0", }, subtitle: { - raw: "Only takes a minute.", + string_id: "create-backup-screen-2-subtitle", + fontSize: "0.8125em", + letterSpacing: "revert", + marginBlock: "8px 0", }, tiles: { type: "single-select", @@ -263,17 +291,17 @@ const MESSAGES = () => [ icon: { background: "center / contain no-repeat url('https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/a43cd9cc-e8b2-477c-92f2-345557370de1.svg')", - width: "60px", - height: "40px", - marginBlockStart: "10px", - paddingBlockEnd: "10px", + width: "54px", + height: "54px", + marginBlockStart: "22px", borderRadius: "5px", }, id: "easy", label: { - raw: "Easy setup", + string_id: "create-backup-screen-2-easy-label", fontSize: 17, fontWeight: 600, + marginBlock: "3px 10px", }, body: { items: [ @@ -285,7 +313,9 @@ const MESSAGES = () => [ width: "18px", }, text: { - raw: "Bookmarks, history, settings, and more", + string_id: "create-backup-screen-2-easy-list-1", + marginBlock: "4px", + fontSize: "13px", }, }, { @@ -296,7 +326,9 @@ const MESSAGES = () => [ width: "18px", }, text: { - raw: "Doesn't include passwords and payments", + string_id: "create-backup-screen-2-easy-list-2", + marginBlock: "4px", + fontSize: "13px", }, }, { @@ -307,17 +339,24 @@ const MESSAGES = () => [ width: "18px", }, text: { - raw: "Not encrypted", - fontWeight: 500, + string_id: "create-backup-screen-2-easy-list-3", + marginBlock: "4px", + fontSize: "13px", + fontWeight: "600", }, }, ], }, tilebutton: { label: { - raw: "Select", + string_id: "create-backup-select-tile-button-label", + minHeight: "24px", + minWidth: "revert", + lineHeight: "100%", + paddingBlock: "4px", + paddingInline: "16px", + marginBlock: "0 16px", }, - marginBlock: "0 16px", style: "primary", action: { type: "SET_PREF", @@ -337,17 +376,17 @@ const MESSAGES = () => [ icon: { background: "center / contain no-repeat url('https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/0ddfd632-b9c4-45d6-86c3-b89f94797110.svg')", - width: "60px", - height: "40px", - marginBlockStart: "10px", - paddingBlockEnd: "10px", + width: "54px", + height: "54px", + marginBlockStart: "22px", borderRadius: "5px", }, id: "all", label: { - raw: "All data", + string_id: "create-backup-screen-2-all-label", fontSize: 17, fontWeight: 600, + marginBlock: "3px 10px", }, body: { items: [ @@ -359,7 +398,9 @@ const MESSAGES = () => [ width: "18px", }, text: { - raw: "Bookmarks, history, settings, and more", + string_id: "create-backup-screen-2-easy-list-1", + marginBlock: "4px", + fontSize: "13px", }, }, { @@ -370,7 +411,9 @@ const MESSAGES = () => [ width: "18px", }, text: { - raw: "Includes passwords and payments", + string_id: "create-backup-screen-2-all-list-2", + marginBlock: "4px", + fontSize: "13px", }, }, { @@ -381,7 +424,9 @@ const MESSAGES = () => [ width: "18px", }, text: { - raw: "Encrypted with a password", + string_id: "create-backup-screen-2-all-list-3", + marginBlock: "4px", + fontSize: "13px", fontWeight: 500, }, }, @@ -389,7 +434,13 @@ const MESSAGES = () => [ }, tilebutton: { label: { - raw: "Select", + string_id: "create-backup-select-tile-button-label", + minHeight: "24px", + minWidth: "revert", + lineHeight: "100%", + paddingBlock: "4px", + paddingInline: "16px", + marginBlock: "0 16px", }, marginBlock: "0 16px", style: "primary", @@ -410,7 +461,13 @@ const MESSAGES = () => [ additional_button: { style: "secondary", label: { - raw: "Back", + string_id: "create-backup-back-button-label", + fontSize: "0.75em", + minHeight: "24px", + minWidth: "revert", + lineHeight: "100%", + paddingBlock: "4px", + paddingInline: "12px", }, action: { navigate: true, @@ -427,25 +484,40 @@ const MESSAGES = () => [ content: { logo: { imageURL: - "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/a3c640c8-7594-4bb2-bc18-8b4744f3aaf2.gif", + "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/0706f067-eaf8-4537-a9e1-6098d990f511.svg", + height: "110px", }, title: { - raw: "Easy Backup", + raw: "Where do you want your backup saved?", paddingBlock: "8px", + fontSize: "24px", + fontWeight: 600, }, - dismiss_button: { - action: { - dismiss: true, - }, + isEncryptedBackup: false, + screen_style: { + width: "643px", }, - primary_button: { - label: "Primary", - action: { - navigate: true, + tiles: { + type: "fx_backup_file_path", + options: { + hide_password_input: true, + file_path_label: "fx-backup-opt-in-filepath-label", + turn_on_backup_header: "fx-backup-opt-in-header", + turn_on_backup_confirm_btn_label: + "fx-backup-opt-in-confirm-btn-label", }, }, - secondary_button: { - label: "Go back", + additional_button: { + style: "secondary", + label: { + raw: "Back", + fontSize: "0.75em", + minHeight: "24px", + minWidth: "revert", + lineHeight: "100%", + paddingBlock: "4px", + paddingInline: "12px", + }, action: { navigate: true, goBack: true, @@ -457,29 +529,92 @@ const MESSAGES = () => [ id: "SCREEN_3B", force_hide_steps_indicator: true, targeting: - "('messaging-system-action.backupChooser' |preferenceValue == 'full' || preferenceValue == null)", + "('messaging-system-action.backupChooser' |preferenceValue == 'full')", content: { logo: { imageURL: - "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/a3c640c8-7594-4bb2-bc18-8b4744f3aaf2.gif", + "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/0706f067-eaf8-4537-a9e1-6098d990f511.svg", + height: "110px", }, title: { - raw: "Full Backup", - paddingBlock: "8px", + raw: "Where do you want your backup saved?", }, - dismiss_button: { - action: { - dismiss: true, + isEncryptedBackup: true, + screen_style: { + width: "643px", + }, + tiles: { + type: "fx_backup_file_path", + options: { + hide_password_input: true, + hide_secondary_button: true, + file_path_label: "fx-backup-opt-in-filepath-label", + turn_on_backup_header: "fx-backup-opt-in-header", + turn_on_backup_confirm_btn_label: + "fx-backup-opt-in-confirm-btn-label", }, }, - primary_button: { - label: "Primary", + additional_button: { + style: "secondary", + flow: "row", + label: { + raw: "Back", + fontSize: "0.75em", + minHeight: "24px", + minWidth: "revert", + lineHeight: "100%", + paddingBlock: "4px", + paddingInline: "12px", + }, action: { navigate: true, + goBack: true, }, }, - secondary_button: { - label: "Go back", + }, + }, + { + id: "FX_BACKUP_ENCRYPTION", + force_hide_steps_indicator: true, + targeting: + "('messaging-system-action.backupChooser' |preferenceValue == 'full')", + content: { + isEncryptedBackup: true, + title: { + raw: "Create a backup file password", + }, + subtitle: { + raw: "Required to encrypt your data. Store it in a place you’ll remember.", + fontSize: "13px", + }, + screen_style: { + width: "700px", + }, + logo: { + imageURL: + "https://firefox-settings-attachments.cdn.mozilla.net/main-workspace/ms-images/0fb332a4-6b15-4d6e-bbd5-0558ac3e004f.svg", + height: "130px", + }, + tiles: { + type: "fx_backup_password", + options: { + hide_secondary_button: true, + create_password_label: "fx-backup-opt-in-create-password-label", + turn_on_backup_confirm_btn_label: + "fx-backup-opt-in-confirm-btn-label", + }, + }, + additional_button: { + style: "secondary", + label: { + raw: "Back", + fontSize: "0.75em", + minHeight: "24px", + minWidth: "revert", + lineHeight: "100%", + paddingBlock: "4px", + paddingInline: "12px", + }, action: { navigate: true, goBack: true, @@ -487,6 +622,29 @@ const MESSAGES = () => [ }, }, }, + { + id: "FX_BACKUP_THANKS", + force_hide_steps_indicator: true, + content: { + isEncryptedBackup: true, + title: { + raw: "Done!", + }, + screen_style: { + width: "700px", + }, + logo: {}, + additional_button: { + style: "primary", + label: { + raw: "Finish", + }, + action: { + navigate: true, + }, + }, + }, + }, ], transitions: true, }, diff --git a/browser/components/backup/content/password-validation-inputs.css b/browser/components/backup/content/password-validation-inputs.css @@ -57,3 +57,12 @@ transform: translateY(calc(var(--input-text-min-height) + 1.75rem)); } } + +:host([embedded-fx-backup-opt-in]) { + #new-password-span, + #repeat-password-span { + text-align: start; + font-weight: var(--font-weight-heading); + font-size: var(--font-size-sub-heading); + } +} diff --git a/browser/components/backup/content/password-validation-inputs.mjs b/browser/components/backup/content/password-validation-inputs.mjs @@ -26,6 +26,11 @@ export default class PasswordValidationInputs extends MozLitElement { */ _tooltipFocus: { type: Boolean, state: true }, supportBaseLink: { type: String }, + createPasswordLabelL10nId: { + type: String, + reflect: true, + attribute: "create-password-label-l10n-id", + }, }; static get queries() { @@ -157,7 +162,8 @@ export default class PasswordValidationInputs extends MozLitElement { <div id="new-password-label-wrapper-span-input"> <span id="new-password-span" - data-l10n-id="enable-backup-encryption-create-password-label" + data-l10n-id=${this.createPasswordLabelL10nId || + "enable-backup-encryption-create-password-label"} ></span> <input type="password" diff --git a/browser/components/backup/content/turn-on-scheduled-backups.css b/browser/components/backup/content/turn-on-scheduled-backups.css @@ -7,6 +7,14 @@ :host { --margin-inline-start-checkbox-content: calc(var(--checkbox-margin-inline) + var(--checkbox-size)); + --font-weight-heading: var(--font-weight-bold, 600); + --font-size-heading: 17px; + --font-size-sub-heading: 15px; + --font-weight-label: var(--font-weight, normal); + --font-size-label: 14px; + --padding-block-wrapper: 20px; + --margin-block-start-file-picker: 18px; + --gap-button-group: 12px; } #backup-turn-on-scheduled-wrapper { @@ -89,3 +97,70 @@ grid-area: button-group; } } + +:host([hide-password-input]) #backup-turn-on-scheduled-wrapper #sensitive-data-controls { + display: none; +} + +:host([hide-file-path-chooser]) #backup-turn-on-scheduled-wrapper #backup-location-controls { + display: none; +} + +:host([hide-file-path-chooser]) #backup-turn-on-scheduled-wrapper #sensitive-data-controls #sensitive-data-checkbox { + display: none; +} + +:host([hide-file-path-chooser]) #backup-turn-on-scheduled-wrapper #backup-turn-on-scheduled-header { + display: none; +} + +:host([hide-buttons]) #backup-turn-on-scheduled-wrapper #backup-turn-on-scheduled-button-group { + display: none; +} + +:host([hide-secondary-button]) #backup-turn-on-scheduled-wrapper #backup-turn-on-scheduled-cancel-button { + display: none; +} + +:host([hide-headers]) #backup-turn-on-scheduled-description { + display: none; +} + +:host([embedded-fx-backup-opt-in]) { + #backup-turn-on-scheduled-wrapper { + width: 500px; + margin-inline: auto; + padding-block: var(--padding-block-wrapper); + + #backup-location-controls #backup-location-filepicker { + margin-block-start: var(--margin-block-start-file-picker); + } + + #backup-turn-on-scheduled-header { + font-weight: var(--font-weight-heading); + font-size: var(--font-size-heading); + text-align: start; + } + + #backup-location-label { + font-size: var(--font-size-label); + text-align: start; + font-weight: var(--font-weight-label); + } + } +} + +:host([embedded-fx-backup-opt-in]:not([hide-buttons])) { + #backup-turn-on-scheduled-wrapper { + #backup-turn-on-scheduled-button-group { + display: flex; + flex-direction: column-reverse; + align-items: center; + gap: var(--gap-button-group); + + #backup-turn-on-scheduled-confirm-button { + margin: 0; + } + } + } +} diff --git a/browser/components/backup/content/turn-on-scheduled-backups.mjs b/browser/components/backup/content/turn-on-scheduled-backups.mjs @@ -36,11 +36,52 @@ export default class TurnOnScheduledBackups extends MozLitElement { #placeholderIconURL = "chrome://global/skin/icons/page-portrait.svg"; static properties = { + backupServiceState: { type: Object }, // passed in from parents defaultIconURL: { type: String, reflect: true }, defaultLabel: { type: String, reflect: true }, defaultPath: { type: String, reflect: true }, supportBaseLink: { type: String }, + embeddedFxBackupOptIn: { + type: Boolean, + reflect: true, + attribute: "embedded-fx-backup-opt-in", + }, + hideFilePathChooser: { + type: Boolean, + reflect: true, + attribute: "hide-file-path-chooser", + }, + hideSecondaryButton: { + type: Boolean, + reflect: true, + attribute: "hide-secondary-button", + }, + filePathLabelL10nId: { + type: String, + reflect: true, + attribute: "file-path-label-l10n-id", + }, + turnOnBackupHeaderL10nId: { + type: String, + reflect: true, + attribute: "turn-on-backup-header-l10n-id", + }, + createPasswordLabelL10nId: { + type: String, + reflect: true, + attribute: "create-password-label-l10n-id", + }, + turnOnBackupConfirmBtnL10nId: { + type: String, + reflect: true, + attribute: "turn-on-backup-confirm-btn-l10n-id", + }, + turnOnBackupCancelBtnL10nId: { + type: String, + reflect: true, + attribute: "turn-on-backup-cancel-btn-l10n-id", + }, // internal state _newIconURL: { type: String, state: true }, @@ -69,6 +110,7 @@ export default class TurnOnScheduledBackups extends MozLitElement { constructor() { super(); + this.backupServiceState = {}; this.defaultIconURL = ""; this.defaultLabel = ""; this.defaultPath = ""; @@ -132,9 +174,20 @@ export default class TurnOnScheduledBackups extends MozLitElement { } handleConfirm() { - let detail = { - parentDirPath: this._newPath || this.defaultPath, - }; + let detail; + if (this._newPath) { + detail = { + parentDirPath: this._newPath, + }; + } else if (this.backupServiceState?.backupDirPath) { + detail = { + parentDirPath: this.backupServiceState?.backupDirPath, + }; + } else { + detail = { + parentDirPath: this.defaultPath, + }; + } if (this._showPasswordOptions && this._passwordsMatch) { detail.password = this._inputPassValue; @@ -153,6 +206,20 @@ export default class TurnOnScheduledBackups extends MozLitElement { this._passwordsMatch = false; } + updated(changedProperties) { + super.updated?.(changedProperties); + + if (changedProperties.has("hideFilePathChooser")) { + // If hideFilePathChooser is true, show password options + this._showPasswordOptions = !!this.hideFilePathChooser; + + // Uncheck the checkbox if it exists + if (this.passwordOptionsCheckboxEl) { + this.passwordOptionsCheckboxEl.checked = this._showPasswordOptions; + } + } + } + reset() { this._newPath = ""; this._newIconURL = ""; @@ -223,7 +290,8 @@ export default class TurnOnScheduledBackups extends MozLitElement { <label id="backup-location-label" for="backup-location-filepicker-input" - data-l10n-id="turn-on-scheduled-backups-location-label" + data-l10n-id=${this.filePathLabelL10nId || + "turn-on-scheduled-backups-location-label"} ></label> <div id="backup-location-filepicker"> ${!this._newPath @@ -237,7 +305,6 @@ export default class TurnOnScheduledBackups extends MozLitElement { ></moz-button> </div> </div> - <fieldset id="sensitive-data-controls"> <div id="sensitive-data-checkbox"> <label @@ -274,6 +341,8 @@ export default class TurnOnScheduledBackups extends MozLitElement { <password-validation-inputs id="passwords" .supportBaseLink=${this.supportBaseLink} + .createPasswordLabelL10nId=${this.createPasswordLabelL10nId} + ?embedded-fx-backup-opt-in=${this.embeddedFxBackupOptIn} ></password-validation-inputs> `; } @@ -284,11 +353,13 @@ export default class TurnOnScheduledBackups extends MozLitElement { id="backup-turn-on-scheduled-wrapper" aria-labelledby="backup-turn-on-scheduled-header" aria-describedby="backup-turn-on-scheduled-description" + part="form" > <h1 id="backup-turn-on-scheduled-header" class="heading-medium" - data-l10n-id="turn-on-scheduled-backups-header" + data-l10n-id=${this.turnOnBackupHeaderL10nId || + "turn-on-scheduled-backups-header"} ></h1> <main id="backup-turn-on-scheduled-content"> <div id="backup-turn-on-scheduled-description"> @@ -312,14 +383,16 @@ export default class TurnOnScheduledBackups extends MozLitElement { <moz-button id="backup-turn-on-scheduled-cancel-button" @click=${this.close} - data-l10n-id="turn-on-scheduled-backups-cancel-button" + data-l10n-id=${this.turnOnBackupCancelBtnL10nId || + "turn-on-scheduled-backups-cancel-button"} ></moz-button> <moz-button id="backup-turn-on-scheduled-confirm-button" form="backup-turn-on-scheduled-wrapper" @click=${this.handleConfirm} type="primary" - data-l10n-id="turn-on-scheduled-backups-confirm-button" + data-l10n-id=${this.turnOnBackupConfirmBtnL10nId || + "turn-on-scheduled-backups-confirm-button"} ?disabled=${this._showPasswordOptions && !this._passwordsMatch} ></moz-button> </moz-button-group> diff --git a/browser/components/backup/content/turn-on-scheduled-backups.stories.mjs b/browser/components/backup/content/turn-on-scheduled-backups.stories.mjs @@ -25,31 +25,40 @@ export default { mapping: SELECTABLE_ERRORS, control: { type: "select" }, }, + hideFilePathChooser: { control: "boolean" }, + embeddedFxBackupOptIn: { control: "boolean" }, + isEncryptedBackup: { control: "boolean" }, }, }; const Template = ({ defaultPath, - _newPath, defaultLabel, + _newPath, _newLabel, enableBackupErrorCode, + hideFilePathChooser, + embeddedFxBackupOptIn, }) => html` - <moz-card style="width: 27.8rem; position: relative;"> - <turn-on-scheduled-backups - defaultPath=${defaultPath} - _newPath=${ifDefined(_newPath)} - defaultLabel=${defaultLabel} - _newLabel=${ifDefined(_newLabel)} - .enableBackupErrorCode=${enableBackupErrorCode} - ></turn-on-scheduled-backups> - </moz-card> + <turn-on-scheduled-backups + defaultPath=${defaultPath} + defaultLabel=${defaultLabel} + _newPath=${ifDefined(_newPath)} + _newLabel=${ifDefined(_newLabel)} + .enableBackupErrorCode=${enableBackupErrorCode} + ?hide-file-path-chooser=${hideFilePathChooser} + ?embedded-fx-backup-opt-in=${embeddedFxBackupOptIn} + ></turn-on-scheduled-backups> `; +// ---------------------- Default / legacy stories ---------------------- export const Default = Template.bind({}); Default.args = { defaultPath: "/Some/User/Documents", defaultLabel: "Documents", + hideFilePathChooser: false, + embeddedFxBackupOptIn: false, + enableBackupErrorCode: 0, }; export const CustomLocation = Template.bind({}); @@ -64,3 +73,18 @@ EnableError.args = { ...CustomLocation.args, enableBackupErrorCode: ERRORS.FILE_SYSTEM_ERROR, }; + +// ---------------------- Embedded Fx Backup Opt-In Stories ---------------------- +export const EmbeddedFx_UnencryptedBackup = Template.bind({}); +EmbeddedFx_UnencryptedBackup.args = { + ...Default.args, + embeddedFxBackupOptIn: true, + hideFilePathChooser: false, // Shows file path chooser, password section hidden via CSS +}; + +export const EmbeddedFx_EncryptedBackup_HideFilePathChooser = Template.bind({}); +EmbeddedFx_EncryptedBackup_HideFilePathChooser.args = { + ...Default.args, + embeddedFxBackupOptIn: true, + hideFilePathChooser: true, // Hide file path chooser, show password input +};