Bug 1408334 - add form to subscribe to mozilla developer newsletter in about:devtools;r=nchevobbe

MozReview-Commit-ID: JuJqNS3r6NH

--HG--
extra : rebase_source : 20b9f294cb60873bfa6edc975d4688c1a2365eef
This commit is contained in:
Julian Descottes 2017-10-31 10:19:00 +01:00
Родитель d39f5742f5
Коммит 10e099779f
7 изменённых файлов: 320 добавлений и 12 удалений

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

@ -1,3 +1,7 @@
/* 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/. */
:root {
/* Photon color variables used on the aboutdevtools page */
--blue-60: #0060df;
@ -8,7 +12,14 @@
--grey-90-alpha-10: rgba(12, 12, 13, 0.1);
--grey-90-alpha-20: rgba(12, 12, 13, 0.2);
--grey-90-alpha-30: rgba(12, 12, 13, 0.3);
--grey-90-alpha-40: rgba(12, 12, 13, 0.4);
--grey-90-alpha-50: rgba(12, 12, 13, 0.5);
--teal-60: #00c8d7;
--red-50: #ff0039;
--white: #ffffff;
/* Shared variables */
--line-height: 1.5em;
}
html, body {
@ -17,16 +28,15 @@ html, body {
}
p {
line-height: 1.5em;
line-height: var(--line-height);
}
.box {
width: 100%;
max-width: 850px;
display: flex;
align-items: center;
height: 400px;
flex-shrink: 0;
padding: 34px 0 50px 0;
}
.wrapper {
@ -38,19 +48,16 @@ p {
}
.left-pane {
width: 360px;
height: 100%;
width: 300px;
height: 300px;
margin-inline-end: 20px;
background-image: url(images/otter.svg);
background-size: 80%;
background-size: 100%;
background-position: 50%;
background-repeat: no-repeat;
flex-shrink: 0;
}
.right-pane {
height: 250px;
}
.features {
max-width: 980px;
border-top: 1px solid var(--grey-30);
@ -129,7 +136,7 @@ p {
}
button {
margin: 2em 0 0 0;
margin: 20px 0 0 0;
padding: 10px 20px;
border: none;

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

@ -14,7 +14,9 @@
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>a
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools-shim/content/aboutdevtools/aboutdevtools.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools-shim/content/aboutdevtools/subscribe.css" type="text/css"/>
<script type="application/javascript" src="chrome://devtools-shim/content/aboutdevtools/aboutdevtools.js"></script>
<script type="application/javascript" src="chrome://devtools-shim/content/aboutdevtools/subscribe.js"></script>
</head>
<body>
<div id="install-page" class="wrapper" hidden="true">
@ -48,6 +50,33 @@
<div class="right-pane">
<h1 class="title" >&aboutDevtools.welcome.title;</h1>
<p id="welcome-message">&aboutDevtools.welcome.message;</p>
<!-- Form dedicated to the newsletter subscription -->
<div class="newsletter">
<h2 class="newsletter-title">&aboutDevtools.newsletter.title;</h2>
<p>&aboutDevtools.newsletter.message;</p>
<form id="newsletter-form" name="newsletter-form" action="https://www.mozilla.org/en-US/newsletter/" method="post">
<!-- "H" stands for the HTML format (->fmt). Alternative is T for text. -->
<input type="hidden" id="fmt" name="fmt" value="H" />
<!-- "app-dev" is the id of the Mozilla Developper newsletter -->
<input type="hidden" id="newsletters" name="newsletters" value="app-dev" />
<div id="newsletter-errors"></div>
<section id="newsletter-email" class="newsletter-form-section">
<input type="email" id="email" name="email" required="true" placeholder="&aboutDevtools.newsletter.email.placeholder;" />
</section>
<section id="newsletter-privacy" class="newsletter-form-section">
<input type="checkbox" id="privacy" name="privacy" required="true" />
<label for="privacy">&aboutDevtools.newsletter.privacy.label;</label>
</section>
<button type="submit" id="newsletter-submit" class="primary-button">&aboutDevtools.newsletter.subscribe.label;</button>
</form>
<div id="newsletter-thanks">
<h2>&aboutDevtools.newsletter.thanks.title;</h2>
<p>&aboutDevtools.newsletter.thanks.message;</p>
</div>
</div>
</div>
</div>

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

@ -0,0 +1,100 @@
/* 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/. */
/**
* This file contains the styles for the newsletter subscription form on about:devtools.
* It is largely inspired from https://mozilla.github.io/basket-example/
*/
#newsletter-errors {
/* Hidden by default */
display: none;
margin-bottom: 20px;
padding: 10px;
border-radius: 2px;
background-color: var(--red-50);
color: var(--white);
}
#newsletter-errors.show {
display: block;
}
#newsletter-errors .error {
margin: 0;
margin-bottom: 10px;
}
#newsletter-errors .error:last-child {
margin-bottom: 0;
}
#newsletter-thanks {
/* Hidden by default */
display: none;
}
#newsletter-thanks.show {
display: block;
}
.newsletter-form-section {
display: block;
margin-bottom: 20px;
width: 320px;
}
#newsletter-privacy {
display: flex;
/* The privacy section is hidden by default and only displayed on focus */
height: 0;
margin-bottom: -20px;
overflow: hidden;
}
#newsletter-privacy.animate {
transition: all 0.25s cubic-bezier(.15,.75,.35,.9);
}
#newsletter-privacy label {
line-height: var(--line-height);
}
#privacy {
width: 20px;
height: 20px;
margin: 2px;
margin-inline-end: 10px;
flex-shrink: 0;
}
#email {
width: 100%;
box-sizing: border-box;
padding: 12px 15px;
}
#newsletter-form input {
border-color: var(--grey-90-alpha-30);
}
#newsletter-form input:hover {
border-color: var(--grey-90-alpha-50);
}
#newsletter-form input:focus {
border-color: var(--teal-60);
box-shadow: 0 0 2px 0 var(--teal-60);
}
#newsletter-form::placeholder {
color: var(--grey-90-alpha-40);
}
#newsletter-submit {
display: block;
}

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

@ -0,0 +1,147 @@
/* 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/. */
"use strict";
/**
* This file handles the newsletter subscription form on about:devtools.
* It is largely inspired from https://mozilla.github.io/basket-example/
*/
window.addEventListener("load", function () {
const { utils: Cu } = Components;
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
// Timeout for the subscribe XHR.
const REQUEST_TIMEOUT = 5000;
const ABOUTDEVTOOLS_STRINGS = "chrome://devtools-shim/locale/aboutdevtools.properties";
const aboutDevtoolsBundle = Services.strings.createBundle(ABOUTDEVTOOLS_STRINGS);
let emailInput = document.getElementById("email");
let newsletterErrors = document.getElementById("newsletter-errors");
let newsletterForm = document.getElementById("newsletter-form");
let newsletterPrivacySection = document.getElementById("newsletter-privacy");
let newsletterThanks = document.getElementById("newsletter-thanks");
/**
* Update the error panel to display the provided errors. If the argument is null or
* empty, a default error message will be displayed.
*
* @param {Array} errors
* Array of strings, each item being an error message to display.
*/
function updateErrorPanel(errors) {
clearErrorPanel();
if (!errors || errors.length == 0) {
errors = [aboutDevtoolsBundle.GetStringFromName("newsletter.error.unknown")];
}
// Create errors markup.
let fragment = document.createDocumentFragment();
for (let error of errors) {
let item = document.createElement("p");
item.classList.add("error");
item.appendChild(document.createTextNode(error));
fragment.appendChild(item);
}
newsletterErrors.appendChild(fragment);
newsletterErrors.classList.add("show");
}
/**
* Hide the error panel and remove all errors.
*/
function clearErrorPanel() {
newsletterErrors.classList.remove("show");
newsletterErrors.innerHTML = "";
}
// Show the additional form fields on focus of the email input.
function onEmailInputFocus() {
// Create a hidden measuring container, append it to the parent of the privacy section
let container = document.createElement("div");
container.style.cssText = "visibility: hidden; overflow: hidden; position: absolute";
newsletterPrivacySection.parentNode.appendChild(container);
// Clone the privacy section, append the clone to the measuring container.
let clone = newsletterPrivacySection.cloneNode(true);
container.appendChild(clone);
// Measure the target height of the privacy section.
clone.style.height = "auto";
let height = clone.offsetHeight;
// Cleanup the measuring container.
container.remove();
// Set the animate class and set the height to the measured height.
newsletterPrivacySection.classList.add("animate");
newsletterPrivacySection.style.cssText = `height: ${height}px; margin-bottom: 0;`;
}
// XHR subscribe; handle errors; display thanks message on success.
function onFormSubmit(evt) {
evt.preventDefault();
evt.stopPropagation();
// New submission, clear old errors
clearErrorPanel();
let xhr = new XMLHttpRequest();
xhr.onload = function (r) {
if (r.target.status >= 200 && r.target.status < 300) {
let {response} = r.target;
if (response.success === true) {
// Hide form and show success message.
newsletterForm.style.display = "none";
newsletterThanks.classList.add("show");
} else {
// We trust the error messages from the service to be meaningful for the user.
updateErrorPanel(response.errors);
}
} else {
let {status, statusText} = r.target;
let statusInfo = `${status} - ${statusText}`;
let error = aboutDevtoolsBundle
.formatStringFromName("newsletter.error.common", [statusInfo], 1);
updateErrorPanel([error]);
}
};
xhr.onerror = () => {
updateErrorPanel();
};
xhr.ontimeout = () => {
let error = aboutDevtoolsBundle.GetStringFromName("newsletter.error.timeout");
updateErrorPanel([error]);
};
let url = newsletterForm.getAttribute("action");
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.timeout = REQUEST_TIMEOUT;
xhr.responseType = "json";
// Create form data.
let formData = new FormData(newsletterForm);
formData.append("source_url", document.location.href);
let params = new URLSearchParams(formData);
// Send the request.
xhr.send(params.toString());
}
// Attach event listeners.
newsletterForm.addEventListener("submit", onFormSubmit);
emailInput.addEventListener("focus", onEmailInputFocus);
}, { once: true });

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

@ -22,4 +22,14 @@
<!ENTITY aboutDevtools.enable.installButton "Enable Developer Tools">
<!ENTITY aboutDevtools.enable.closeButton "Close this page">
<!ENTITY aboutDevtools.welcome.title "Welcome to Firefox Developer Tools!">
<!ENTITY aboutDevtools.welcome.message "Youve successfully enabled DevTools! To get started, explore the Web Developer menu or open the tools with ##INSPECTOR_SHORTCUT##.">
<!ENTITY aboutDevtools.welcome.message "Youve successfully enabled DevTools! To get started, explore the Web Developer menu or open the tools with ##INSPECTOR_SHORTCUT##.">
<!ENTITY aboutDevtools.newsletter.title "Mozilla Developer Newsletter">
<!ENTITY aboutDevtools.newsletter.message "Get developer news, tricks and resources sent straight to your inbox.">
<!ENTITY aboutDevtools.newsletter.email.placeholder "Email">
<!ENTITY aboutDevtools.newsletter.privacy.label "I'm okay with Mozilla handling my info as explained in this <a class='external' href='https://www.mozilla.org/privacy/'>Privacy Policy</a>.">
<!ENTITY aboutDevtools.newsletter.subscribe.label "Subscribe">
<!ENTITY aboutDevtools.newsletter.thanks.title "Thanks!">
<!ENTITY aboutDevtools.newsletter.thanks.message "If you havent previously confirmed a subscription to a Mozilla-related newsletter you may have to do so. Please check your inbox or your spam filter for an email from us.">

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

@ -7,6 +7,8 @@ devtools-shim.jar:
content/aboutdevtools/aboutdevtools.xhtml (aboutdevtools/aboutdevtools.xhtml)
content/aboutdevtools/aboutdevtools.css (aboutdevtools/aboutdevtools.css)
content/aboutdevtools/aboutdevtools.js (aboutdevtools/aboutdevtools.js)
content/aboutdevtools/subscribe.css (aboutdevtools/subscribe.css)
content/aboutdevtools/subscribe.js (aboutdevtools/subscribe.js)
content/aboutdevtools/images/otter.svg (aboutdevtools/images/otter.svg)

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

@ -35,3 +35,16 @@ features.performance.desc=Unblock bottlenecks, streamline processes, optimize as
features.memory.title=Memory
features.memory.desc=Find memory leaks and make your application zippy.
# LOCALIZATION NOTE (newsletter.error.common): error text displayed when the newsletter
# subscription failed. The argument will be replaced with request's status code and status
# text (e.g. "404 - Not Found")
newsletter.error.common=Subscription request failed (%S).
# LOCALIZATION NOTE (newsletter.error.unknown): error text displayed when the newsletter
# subscription failed for an unexpected reason.
newsletter.error.unknown=An unexpected error occurred.
# LOCALIZATION NOTE (newsletter.error.timeout): error text displayed when the newsletter
# subscription timed out.
newsletter.error.timeout=Subscription request timed out.