Skip to content

Commit

Permalink
Merge pull request #1342 from Datawheel/optimize-support
Browse files Browse the repository at this point in the history
[core] Support Google Optimize
  • Loading branch information
jhmullen authored Jan 27, 2022
2 parents 5f9de08 + 98a5cd4 commit a80c8f4
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 19 deletions.
1 change: 1 addition & 0 deletions packages/core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -872,6 +872,7 @@ export CANON_LANGUAGE_DEFAULT="es"
|`CANON_BASE_URL`|If hosting assets or running the server from a different location that the project folder, this variable can be used to define the base URL for all static assets. A `<base>` tag will be added to the start of the `<head>` tag.|`undefined`|
|`CANON_GOOGLE_ANALYTICS`|The unique Google Analytics ID for the project (ex. `"UA-########-#"`). This also supports comma-separated values, if it's desired for pageviews to be reported to multiple analytics properties.|`undefined`|
|`CANON_FACEBOOK_PIXEL`|The unique Facebook Pixel ID for the project (ex. `"################"`).|`undefined`|
|`CANON_GOOGLE_OPTIMIZE`|The unique Google Optimize ID to run user tests. It loads the [synchronous version](https://support.google.com/optimize/answer/9692472]). Read more [here](https://optimize.google.com): (ex. `"OPT-#######"`).|`undefined`|
|`CANON_GDPR`|When set to `true`, tracking services like Google Analytics and Faceboook Pixel will only be loaded after the user accepts the usage of cookies and tracking. A pop-up Drawer will be shown on the bottom of the screen containing text and buttons that are editable in the locales JSON file.|`false`|
|`CANON_GDPR_WAIT`|By default, if a user has not chosen whether to accept or reject the GDPR message, we will assume consent and not ask on subsequent page visits. If this env var is set to `true`, tracking services will only execute after the user has explicitly accepted.|`false`|
|`CANON_GOOGLE_TAG_MANAGER`|The unique Google Tag Manager ID for the project (ex. `"GTM-#######"`).|`undefined`|
Expand Down
18 changes: 16 additions & 2 deletions packages/core/src/helpers/services.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,17 @@ const serviceJavaScript = {
})(window,document,'https://static.hotjar.com/c/hotjar-','.js?sv=');`
};

const serviceHeadTag = {
GOOGLE_OPTIMIZE: id => `<script src="https://www.googleoptimize.com/optimize.js?id=${id}"></script>`
}

const serviceHTML = {
GOOGLE_TAG_MANAGER: id => `<noscript>
<iframe src="https://www.googletagmanager.com/ns.html?id=${id}" height="0" width="0" style="display:none;visibility:hidden"></iframe>
</noscript>`
};

const servicesAvailable = Object.keys(serviceJavaScript).filter(s => process.env[`CANON_${s}`] !== undefined);
const servicesAvailable = Object.keys(serviceJavaScript).filter(s => [undefined, ''].indexOf(process.env[`CANON_${s}`]) === -1);

const servicesScript = servicesAvailable
.map(s => `
Expand All @@ -51,4 +55,14 @@ const servicesBody = servicesAvailable
<!-- End ${titleCase(s.replace(/\_/g, " "))} -->
`).join("\n");

export {servicesAvailable, servicesBody, servicesScript};
// Services that needs a JS tags scripts
const servicesHeadTagsAvailable = Object.keys(serviceHeadTag).filter(s => [undefined, ''].indexOf(process.env[`CANON_${s}`]) === -1);

const servicesHeadTags = servicesHeadTagsAvailable
.map(s => `
<!-- ${titleCase(s.replace(/\_/g, " "))} -->
${serviceHeadTag[s](process.env[`CANON_${s}`])}
<!-- End ${titleCase(s.replace(/\_/g, " "))} -->
`).join("\n");

export {servicesAvailable, servicesBody, servicesScript, servicesHeadTags};
38 changes: 21 additions & 17 deletions packages/core/src/server.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {initialState as appInitialState} from "$app/store";
import preRenderMiddleware from "./middlewares/preRenderMiddleware";
import pretty from "pretty";
import maybeRedirect from "./helpers/maybeRedirect";
import {servicesAvailable, servicesBody, servicesScript} from "./helpers/services";
import {servicesAvailable, servicesBody, servicesScript, servicesHeadTags} from "./helpers/services";
import yn from "yn";

import CanonProvider from "./CanonProvider";
Expand All @@ -30,27 +30,27 @@ const BASE_URL = process.env.CANON_BASE_URL || "/";
const basename = BASE_URL.replace(/^[A-z]{4,5}\:\/{2}[A-z0-9\.\-]{1,}\:{0,}[0-9]{0,4}/g, "");
const baseTag = process.env.CANON_BASE_URL === undefined ? ""
: `
<base href='${ BASE_URL }'>`;
<base href='${BASE_URL}'>`;

/**
Returns the default server logic for rendering a page.
*/
export default function(defaultStore = appInitialState, headerConfig, reduxMiddleware = false) {
export default function (defaultStore = appInitialState, headerConfig, reduxMiddleware = false) {

return function(req, res) {
return function (req, res) {

const locale = req.i18n.language,
resources = req.i18n.getResourceBundle(req.i18n.language);
resources = req.i18n.getResourceBundle(req.i18n.language);

const windowLocation = {
basename,
host: req.headers.host,
hostname: req.headers.host.split(":")[0],
href: `${ req.protocol }://${ req.headers.host }${ req.url }`,
origin: `${ req.protocol }://${ req.headers.host }`,
href: `${req.protocol}://${req.headers.host}${req.url}`,
origin: `${req.protocol}://${req.headers.host}`,
pathname: req.url.split("?")[0],
port: req.headers.host.includes(":") ? req.headers.host.split(":")[1] : "80",
protocol: `${ req.protocol }:`,
protocol: `${req.protocol}:`,
query: req.query,
search: req.url.includes("?") ? `?${req.url.split("?")[1]}` : ""
};
Expand Down Expand Up @@ -120,8 +120,8 @@ export default function(defaultStore = appInitialState, headerConfig, reduxMiddl
const helmetContext = {};

let componentHTML,
scriptTags = "<script type=\"text/javascript\" charset=\"utf-8\" src=\"/assets/client.js\"></script>",
styleTags = "<link rel=\"stylesheet\" type=\"text/css\" href=\"/assets/styles.css\">";
scriptTags = "<script type=\"text/javascript\" charset=\"utf-8\" src=\"/assets/client.js\"></script>",
styleTags = "<link rel=\"stylesheet\" type=\"text/css\" href=\"/assets/styles.css\">";

if (production) {

Expand Down Expand Up @@ -152,7 +152,7 @@ export default function(defaultStore = appInitialState, headerConfig, reduxMiddl
.replace(/\n/g, "\n ");

const cssOrder = ["normalize", "blueprint", "canon"];
const cssRegex = RegExp(`(?:${ cssOrder.join("|") })`);
const cssRegex = RegExp(`(?:${cssOrder.join("|")})`);

styleTags = extractor
.getStyleTags()
Expand Down Expand Up @@ -198,26 +198,30 @@ export default function(defaultStore = appInitialState, headerConfig, reduxMiddl
const serialize = obj => `JSON.parse('${jsesc(JSON.stringify(obj))}')`;

return res.status(status).send(`<!doctype html>
<html dir="${ rtl ? "rtl" : "ltr" }" ${htmlAttrs}${defaultAttrs}>
<html dir="${rtl ? "rtl" : "ltr"}" ${htmlAttrs}${defaultAttrs}>
<head>
${baseTag}
${ pretty(header.title.toString()).replace(/\n/g, "\n ") }
${ pretty(header.meta.toString()).replace(/\n/g, "\n ") }
${servicesHeadTags}
${pretty(header.title.toString()).replace(/\n/g, "\n ")}
${pretty(header.meta.toString()).replace(/\n/g, "\n ")}
${ pretty(header.link.toString()).replace(/\n/g, "\n ") }
${pretty(header.link.toString()).replace(/\n/g, "\n ")}
${styleTags}
</head>
<body>
${servicesBody}
<div id="React-Container">${ componentHTML }</div>
<div id="React-Container">${componentHTML}</div>
<script>
window.__SSR__ = true;
window.__APP_NAME__ = "${ req.i18n.options.defaultNS }";
window.__APP_NAME__ = "${req.i18n.options.defaultNS}";
window.__HELMET_DEFAULT__ = ${serialize(headerConfig)};
window.__INITIAL_STATE__ = ${serialize(initialState)};
${GDPR ? `
Expand Down

0 comments on commit a80c8f4

Please sign in to comment.