Skip to content

Commit

Permalink
[CORL-181] Comment Count Injection (coralproject#2581)
Browse files Browse the repository at this point in the history
* feat: inject comment counts

* fix: tests

* feat: integrate stream embed with coral counts

* chore: refactor constants

* test: test for count bundle

* test: test live comment count integration with stream embed

* feat: use defer

* fix: snapshot

* feat: auto add count.js in when calling Coral.createStreamEmbed

* fix: tests

* fix: rm duplicate test

* chore: remove unuse file
  • Loading branch information
cvle authored and wyattjoh committed Oct 1, 2019
1 parent 53fa5f4 commit 808b355
Show file tree
Hide file tree
Showing 48 changed files with 888 additions and 50 deletions.
1 change: 1 addition & 0 deletions config/jest/client.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
"^coral-account/(.*)$": "<rootDir>/src/core/client/account/$1",
"^coral-admin/(.*)$": "<rootDir>/src/core/client/admin/$1",
"^coral-auth/(.*)$": "<rootDir>/src/core/client/auth/$1",
"^coral-count/(.*)$": "<rootDir>/src/core/client/count/$1",
"^coral-ui/(.*)$": "<rootDir>/src/core/client/ui/$1",
"^coral-stream/(.*)$": "<rootDir>/src/core/client/stream/$1",
"^coral-framework/(.*)$": "<rootDir>/src/core/client/framework/$1",
Expand Down
39 changes: 39 additions & 0 deletions src/core/build/createWebpackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -752,5 +752,44 @@ export default function createWebpackConfig(
),
]),
},
/* Webpack config for count */
{
...baseConfig,
optimization: {
...baseConfig.optimization,
// Ensure that we never split the count into chunks.
splitChunks: {
chunks: "async",
},
// We can turn on sideEffects here as we don't use
// css here and don't run into: https://github.com/webpack/webpack/issues/7094
sideEffects: true,
},
entry: [paths.appCountIndex],
output: {
...baseConfig.output,
// don't hash the count, cache-busting must be completed by the requester
// as this lives in a static template on the embed site.
filename: "assets/js/count.js",
},
plugins: filterPlugins([
...baseConfig.plugins!,
...ifWatch(
// Generates an `embed.html` file with the <script> injected.
new HtmlWebpackPlugin({
filename: "count.html",
template: paths.appCountHTML,
inject: "body",
})
),
...ifBuild(
new WebpackAssetsManifest({
output: "count-asset-manifest.json",
entrypoints: true,
integrity: true,
})
),
]),
},
];
}
3 changes: 3 additions & 0 deletions src/core/build/paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ export default {
appAuthCallbackHTML: resolveSrc("core/client/auth-callback/index.html"),
appAuthCallbackIndex: resolveSrc("core/client/auth-callback/index.ts"),

appCountHTML: resolveSrc("core/client/count/index.html"),
appCountIndex: resolveSrc("core/client/count/index.ts"),

appInstallHTML: resolveSrc("core/client/install/index.html"),
appInstallLocalesTemplate: resolveSrc("core/client/install/locales.ts"),
appInstallIndex: resolveSrc("core/client/install/index.tsx"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { stripIndent } from "common-tags";
import { Localized } from "fluent-react/compat";
import React, { FunctionComponent, useMemo } from "react";

import { getLocationOrigin } from "coral-common/utils";
import { CopyButton } from "coral-framework/components";
import { GetMessage, withGetMessage } from "coral-framework/lib/i18n";
import { getLocationOrigin } from "coral-framework/utils";
import { HorizontalGutter, Typography } from "coral-ui/components";

import Header from "../../Header";
Expand Down Expand Up @@ -56,6 +56,8 @@ const EmbedCode: FunctionComponent<Props> = ({ staticURI, getMessage }) => {
(function() {
var d = document, s = d.createElement('script');
s.src = '${script}/assets/js/embed.js';
s.async = false;
s.defer = true;
s.onload = function() {
Coral.createStreamEmbed({
id: "coral_thread",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,12 +138,14 @@ each of your site’s stories.
<textarea
className="EmbedCode-textArea"
readOnly={true}
rows={22}
rows={24}
value="<div id=\\"coral_thread\\"></div>
<script type=\\"text/javascript\\">
(function() {
var d = document, s = d.createElement('script');
s.src = 'http://localhost/assets/js/embed.js';
s.async = false;
s.defer = true;
s.onload = function() {
Coral.createStreamEmbed({
id: \\"coral_thread\\",
Expand Down
27 changes: 27 additions & 0 deletions src/core/client/count/getCurrentScriptOrigin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* getCurrentScriptOrigin will try to find the script origin.
* For legacy browsers a fallbackIdentifier is required.
*
* @argument fallbackID id attached to a script tag to get its origin from for legacy browsrs.
*/
function getCurrentScriptOrigin(fallbackID?: string) {
// Find current script (modern browsers).
let script = document.currentScript as HTMLScriptElement | null;

if (!script && fallbackID) {
// Find script tag with `fallbackIdentifier` as its id.
script = document.getElementById(fallbackID) as HTMLScriptElement | null;
if (!script) {
// Find script tag with `fallbackIdentifier` as its className.
script = document.querySelector(
`.${fallbackID}`
) as HTMLScriptElement | null;
}
}
if (!script) {
throw new Error("Current script not found");
}
return new URL(script.src).origin;
}

export default getCurrentScriptOrigin;
23 changes: 23 additions & 0 deletions src/core/client/count/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<title>Coral - Count</title>
<meta charset="utf-8" />
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, user-scalable=no" />
<link rel="canonical" href="http://localhost:8080/" />
</head>

<body>
<h1>Comment Counts</h1>
<h3>Specified by canonical link</h3>
<p><a href="http://localhost:8080/">Default: <span class="coral-count"></span></a></p>

<h3>Specified by data-coral-url</h3>
<p><a href="http://localhost:8080/story.html">Story: <span class="coral-count" data-coral-url="http://localhost:8080/story.html"></span></a></p>
<p><a href="http://localhost:8080/storyButton.html">Story With Button: <span class="coral-count" data-coral-url="http://localhost:8080/storyButton.html"></span></a></p>

<h3>No text</h3>
<p><a href="http://localhost:8080/">Default notext: <span class="coral-count" data-coral-notext="true" data-coral-url="http://localhost:8080/"></span></a></p>
</body>
</html>
61 changes: 61 additions & 0 deletions src/core/client/count/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import { COUNT_SELECTOR, ORIGIN_FALLBACK_ID } from "coral-framework/constants";
import detectCountScript from "coral-framework/helpers/detectCountScript";
import resolveStoryURL from "coral-framework/helpers/resolveStoryURL";
import jsonp from "coral-framework/utils/jsonp";

import getCurrentScriptOrigin from "./getCurrentScriptOrigin";
import injectJSONPCallback from "./injectJSONPCallback";

/** Arguments that will be send to the server. */
interface CountQueryArgs {
id?: string;
url?: string;
notext?: boolean;
}

/** createCountQueryRef creates a unique reference from the query args */
function createCountQueryRef(args: CountQueryArgs) {
return btoa(`${JSON.stringify(!!args.notext)};${args.id || args.url}`);
}

/** Detects count elements and use jsonp to inject the counts. */
function detectAndInject() {
const ORIGIN = getCurrentScriptOrigin(ORIGIN_FALLBACK_ID);
const STORY_URL = resolveStoryURL();
/** A map of references pointing to the count query arguments */
const queryMap: Record<string, CountQueryArgs> = {};

// Find all the selected elements and fill the queryMap.
const elements = document.querySelectorAll(COUNT_SELECTOR);
Array.prototype.forEach.call(elements, (element: HTMLElement) => {
let url = element.dataset.coralUrl;
const id = element.dataset.coralId;
const notext = element.dataset.coralNotext === "true";
if (!url && !id) {
url = STORY_URL;
element.dataset.coralUrl = STORY_URL;
}
const args = { id, url, notext };
const ref = createCountQueryRef(args);
if (!(ref in queryMap)) {
queryMap[ref] = args;
}
element.dataset.coralRef = ref;
});

// Call server using JSONP.
Object.keys(queryMap).forEach(ref => {
const { url, id, notext } = queryMap[ref];
const args = { url, id, notext: notext ? "true" : "false", ref };
jsonp(`${ORIGIN}/api/story/count.js`, "CoralCount.setCount", args);
});
}

export function main() {
injectJSONPCallback();
detectAndInject();
}

if (!detectCountScript() && process.env.NODE_ENV !== "test") {
main();
}
18 changes: 18 additions & 0 deletions src/core/client/count/injectJSONPCallback.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { COUNT_SELECTOR } from "coral-framework/constants";

/** Injects a global CoralCount callback into the window object to be used in JSONP */
function injectJSONPCallback() {
(window as any).CoralCount = {
setCount: (data: { ref: string; html: string }) => {
// Find all the elements with ref.
const elements = document.querySelectorAll(
`${COUNT_SELECTOR}[data-coral-ref='${data.ref}']`
);
Array.prototype.forEach.call(elements, (element: HTMLElement) => {
element.innerHTML = data.html;
});
},
};
}

export default injectJSONPCallback;
95 changes: 95 additions & 0 deletions src/core/client/count/test/__snapshots__/basic.spec.ts.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`Calls JSONP 1`] = `
<body>
<span
class="coral-count"
data-coral-ref="ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwL3N0b3J5Lmh0bWw="
data-coral-url="http://localhost:8080/story.html"
/>
<span
class="coral-count"
data-coral-id="1234-5678-91021"
data-coral-ref="ZmFsc2U7MTIzNC01Njc4LTkxMDIx"
/>
<span
class="coral-count"
data-coral-ref="ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwLw=="
data-coral-url="http://localhost:8080/"
data-notext="true"
/>
<span
class="coral-count"
data-coral-ref="ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwLw=="
data-coral-url="http://localhost:8080/"
/>
<script
src="http://localhost:8080/api/story/count.js?callback=CoralCount.setCount&url=http%3A%2F%2Flocalhost%3A8080%2Fstory.html&notext=false&ref=ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwL3N0b3J5Lmh0bWw%3D"
/>
<script
src="http://localhost:8080/api/story/count.js?callback=CoralCount.setCount&id=1234-5678-91021&notext=false&ref=ZmFsc2U7MTIzNC01Njc4LTkxMDIx"
/>
<script
src="http://localhost:8080/api/story/count.js?callback=CoralCount.setCount&url=http%3A%2F%2Flocalhost%3A8080%2F&notext=false&ref=ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwLw%3D%3D"
/>
</body>
`;

exports[`Inject counts 1`] = `
<body>
<span
class="coral-count"
data-coral-ref="ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwL3N0b3J5Lmh0bWw="
data-coral-url="http://localhost:8080/story.html"
/>
<span
class="coral-count"
data-coral-id="1234-5678-91021"
data-coral-ref="ZmFsc2U7MTIzNC01Njc4LTkxMDIx"
/>
<span
class="coral-count"
data-coral-ref="ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwLw=="
data-coral-url="http://localhost:8080/"
data-notext="true"
>
<span
class="coral-count-number"
>
5
</span>
<span
class="coral-count-text"
>
Comments
</span>
</span>
<span
class="coral-count"
data-coral-ref="ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwLw=="
data-coral-url="http://localhost:8080/"
>
<span
class="coral-count-number"
>
5
</span>
<span
class="coral-count-text"
>
Comments
</span>
</span>
<script
src="http://localhost:8080/api/story/count.js?callback=CoralCount.setCount&url=http%3A%2F%2Flocalhost%3A8080%2Fstory.html&notext=false&ref=ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwL3N0b3J5Lmh0bWw%3D"
/>
<script
src="http://localhost:8080/api/story/count.js?callback=CoralCount.setCount&id=1234-5678-91021&notext=false&ref=ZmFsc2U7MTIzNC01Njc4LTkxMDIx"
/>
<script
src="http://localhost:8080/api/story/count.js?callback=CoralCount.setCount&url=http%3A%2F%2Flocalhost%3A8080%2F&notext=false&ref=ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwLw%3D%3D"
/>
</body>
`;
53 changes: 53 additions & 0 deletions src/core/client/count/test/basic.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
beforeAll(async () => {
const script = document.createElement("script");
script.src = "http://localhost:8080/assets/js/count.js";
Object.defineProperty(window.document, "currentScript", {
value: script,
});

const link = document.createElement("link");
link.rel = "canonical";
link.href = "http://localhost:8080/";
document.head.appendChild(link);
});

beforeEach(async () => {
document.body.innerHTML = "";
const tags = [
{
coralUrl: "http://localhost:8080/story.html",
},
{
coralId: "1234-5678-91021",
},
{
notext: "true",
},
{},
];

tags.forEach(attrs => {
const element = document.createElement("span");
element.className = "coral-count";
Object.assign(element.dataset, attrs);
document.body.appendChild(element);
});
(await import("../")).main();
});

it("Sets the JSONP callback", async () => {
expect((window as any).CoralCount).toBeDefined();
});

it("Calls JSONP", async () => {
expect(document.body).toMatchSnapshot();
});

it("Inject counts", async () => {
(window as any).CoralCount.setCount({
ref: "ZmFsc2U7aHR0cDovL2xvY2FsaG9zdDo4MDgwLw==",
html:
'<span class="coral-count-number">5</span> <span class="coral-count-text">Comments</span>',
});
expect(document.body).toMatchSnapshot();
});
Loading

0 comments on commit 808b355

Please sign in to comment.