@saasquatch/squatch-js 2.5.0 → 2.5.1-0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +326 -326
- package/LICENSE +20 -20
- package/README.md +145 -145
- package/demo/sandbox.ts +124 -124
- package/demo/toolbar.tsx +526 -526
- package/dist/squatch.esm.js +1 -1
- package/dist/squatch.js +1 -1
- package/dist/squatch.min.js +1 -1
- package/dist/squatch.min.js.map +1 -1
- package/dist/squatch.modern.js +1 -1
- package/dist/squatch.modern.js.map +1 -1
- package/dist/stats.html +1 -1
- package/package.json +105 -105
package/demo/toolbar.tsx
CHANGED
|
@@ -1,526 +1,526 @@
|
|
|
1
|
-
import React, { Component, useRef, useState, version } from "react";
|
|
2
|
-
import { render } from "react-dom";
|
|
3
|
-
import squatch from "../dist/squatch";
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
popup,
|
|
7
|
-
embed,
|
|
8
|
-
embedNew,
|
|
9
|
-
embedReferred,
|
|
10
|
-
popupNew,
|
|
11
|
-
popupReferred,
|
|
12
|
-
script,
|
|
13
|
-
toURL,
|
|
14
|
-
users,
|
|
15
|
-
href,
|
|
16
|
-
} from "./sandbox";
|
|
17
|
-
import { getVersions } from "./versions";
|
|
18
|
-
import { delay } from "./util";
|
|
19
|
-
import { widgets, worker } from "./generate";
|
|
20
|
-
import { rest } from "msw";
|
|
21
|
-
|
|
22
|
-
// 2. Define request handlers and response resolvers.
|
|
23
|
-
|
|
24
|
-
const modes = ["POPUP", "EMBED"];
|
|
25
|
-
const widgetTypes = [
|
|
26
|
-
"REFERRER_WIDGET",
|
|
27
|
-
"CONVERSION_WIDGET",
|
|
28
|
-
"p/tuesday-test/w/referrerWidget",
|
|
29
|
-
];
|
|
30
|
-
const staticVersions = ["HEAD", "latest", "alpha", "next", "local"];
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* Use the addUrlProps higher-order component to hook-in react-url-query.
|
|
34
|
-
*/
|
|
35
|
-
class App extends Component {
|
|
36
|
-
constructor(props) {
|
|
37
|
-
super(props);
|
|
38
|
-
worker.start({
|
|
39
|
-
findWorker: (scriptURL, _mockServiceWorkerUrl) =>
|
|
40
|
-
scriptURL.includes("mockServiceWorker"),
|
|
41
|
-
onUnhandledRequest(req) {
|
|
42
|
-
console.error(
|
|
43
|
-
"Found an unhandled %s request to %s",
|
|
44
|
-
req.method,
|
|
45
|
-
req.url.href
|
|
46
|
-
);
|
|
47
|
-
},
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
state = {
|
|
51
|
-
versions: staticVersions,
|
|
52
|
-
toolbarOpen: true,
|
|
53
|
-
};
|
|
54
|
-
async componentWillMount() {
|
|
55
|
-
const apiVersions = await getVersions();
|
|
56
|
-
const versions = [...staticVersions, ...apiVersions];
|
|
57
|
-
this.setState({ versions });
|
|
58
|
-
}
|
|
59
|
-
render() {
|
|
60
|
-
return (
|
|
61
|
-
<div>
|
|
62
|
-
<button
|
|
63
|
-
onClick={() =>
|
|
64
|
-
this.setState({ toolbarOpen: !this.state.toolbarOpen })
|
|
65
|
-
}
|
|
66
|
-
style={{ float: "right" }}
|
|
67
|
-
>
|
|
68
|
-
{this.state.toolbarOpen ? `<` : `>`}
|
|
69
|
-
</button>
|
|
70
|
-
<div style={{ display: this.state.toolbarOpen ? "block" : "none" }}>
|
|
71
|
-
<hr />
|
|
72
|
-
<div>
|
|
73
|
-
<ParamArea />
|
|
74
|
-
<hr />
|
|
75
|
-
<h2>Quick pick variables</h2>
|
|
76
|
-
<details>
|
|
77
|
-
<summary>Tenant / Program</summary>
|
|
78
|
-
<ul>
|
|
79
|
-
<li>
|
|
80
|
-
<a href={href(popup)}>Popup (classic)</a>
|
|
81
|
-
</li>
|
|
82
|
-
<li>
|
|
83
|
-
<a href={href(embed)}>Embed (classic)</a>
|
|
84
|
-
</li>
|
|
85
|
-
<li>
|
|
86
|
-
<a href={href(popupNew)}>Popup (new program)</a>
|
|
87
|
-
</li>
|
|
88
|
-
<li>
|
|
89
|
-
<a href={href(embedNew)}>Embed (new program)</a>
|
|
90
|
-
</li>
|
|
91
|
-
<li>
|
|
92
|
-
<a href={href(popupReferred)}>
|
|
93
|
-
Popup (classic referred widget)
|
|
94
|
-
</a>
|
|
95
|
-
</li>
|
|
96
|
-
<li>
|
|
97
|
-
<a href={href(embedReferred)}>
|
|
98
|
-
Embed (classic referred widget)
|
|
99
|
-
</a>
|
|
100
|
-
</li>
|
|
101
|
-
</ul>
|
|
102
|
-
</details>
|
|
103
|
-
<WidgetType />
|
|
104
|
-
<ModeList />
|
|
105
|
-
<UserList />
|
|
106
|
-
<VersionList {...this.state} />
|
|
107
|
-
<MockedWidgets />
|
|
108
|
-
<CustomMockedWidget />
|
|
109
|
-
</div>
|
|
110
|
-
<hr />
|
|
111
|
-
|
|
112
|
-
<button onClick={() => recordPurchase()}>Record Purchase</button>
|
|
113
|
-
<hr />
|
|
114
|
-
|
|
115
|
-
<button onClick={() => runEventBomb()}>Event Bomb</button>
|
|
116
|
-
</div>
|
|
117
|
-
</div>
|
|
118
|
-
);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function ParamArea() {
|
|
123
|
-
return (
|
|
124
|
-
<div>
|
|
125
|
-
<h2>Squatch.js Config</h2>
|
|
126
|
-
<div>
|
|
127
|
-
<textarea id="area1" rows={15} cols={70} style={{ maxWidth: "100%" }}>
|
|
128
|
-
{JSON.stringify(window["sandbox"], null, 2)}
|
|
129
|
-
</textarea>
|
|
130
|
-
</div>
|
|
131
|
-
<div>
|
|
132
|
-
<button
|
|
133
|
-
onClick={() => {
|
|
134
|
-
let json: Sandbox = JSON.parse(
|
|
135
|
-
// @ts-ignore
|
|
136
|
-
document.getElementById("area1").value
|
|
137
|
-
);
|
|
138
|
-
toURL(json);
|
|
139
|
-
}}
|
|
140
|
-
>
|
|
141
|
-
Reload Config
|
|
142
|
-
</button>
|
|
143
|
-
<button
|
|
144
|
-
onClick={() => {
|
|
145
|
-
navigator.clipboard.writeText(window.location.toString());
|
|
146
|
-
}}
|
|
147
|
-
>
|
|
148
|
-
Share Current Config
|
|
149
|
-
</button>
|
|
150
|
-
</div>
|
|
151
|
-
</div>
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
async function recordPurchase() {
|
|
156
|
-
//@ts-ignore
|
|
157
|
-
const { squatch, sandbox } = window;
|
|
158
|
-
const {
|
|
159
|
-
jwt,
|
|
160
|
-
user: { id, accountId },
|
|
161
|
-
} = sandbox.initObj;
|
|
162
|
-
const fields = {
|
|
163
|
-
// Optional
|
|
164
|
-
total: 10.0,
|
|
165
|
-
revenue: 10.0,
|
|
166
|
-
tax: 5.0,
|
|
167
|
-
currency: "USD",
|
|
168
|
-
};
|
|
169
|
-
|
|
170
|
-
await squatch.events().track(
|
|
171
|
-
{
|
|
172
|
-
userId: id,
|
|
173
|
-
accountId: accountId,
|
|
174
|
-
events: [
|
|
175
|
-
{
|
|
176
|
-
key: "purchase",
|
|
177
|
-
fields: fields, // Optional
|
|
178
|
-
// id: "kjv12kbwktb13t3", // Optional id
|
|
179
|
-
// dateTriggered: 1535136384753 // Optional date
|
|
180
|
-
},
|
|
181
|
-
],
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
jwt,
|
|
185
|
-
}
|
|
186
|
-
);
|
|
187
|
-
// TODO: Eventually we'd like an API like this:
|
|
188
|
-
// squatch.events().track("purchase", { ...fields });
|
|
189
|
-
}
|
|
190
|
-
async function runEventBomb() {
|
|
191
|
-
while (true) {
|
|
192
|
-
await recordPurchase();
|
|
193
|
-
await delay(100);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
function WidgetType(props) {
|
|
197
|
-
return (
|
|
198
|
-
<details
|
|
199
|
-
title={window["sandbox"].initObj.widgetType}
|
|
200
|
-
key={0}
|
|
201
|
-
id={`dropdown-basic-1`}
|
|
202
|
-
>
|
|
203
|
-
{widgetTypes.map((widgetType, i) => (
|
|
204
|
-
<a
|
|
205
|
-
key={i}
|
|
206
|
-
href={href({
|
|
207
|
-
...window["sandbox"],
|
|
208
|
-
initObj: {
|
|
209
|
-
...window["sandbox"].initObj,
|
|
210
|
-
widgetType: widgetType,
|
|
211
|
-
},
|
|
212
|
-
})}
|
|
213
|
-
>
|
|
214
|
-
{widgetType}
|
|
215
|
-
</a>
|
|
216
|
-
))}
|
|
217
|
-
</details>
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
function ModeList(props) {
|
|
221
|
-
return (
|
|
222
|
-
<details
|
|
223
|
-
title={window["sandbox"].initObj.engagementMedium}
|
|
224
|
-
key={0}
|
|
225
|
-
id={`dropdown-basic-1`}
|
|
226
|
-
>
|
|
227
|
-
<summary>Engagement Medium</summary>
|
|
228
|
-
{modes.map((mode, i) => (
|
|
229
|
-
<a
|
|
230
|
-
key={i}
|
|
231
|
-
href={href({
|
|
232
|
-
...window["sandbox"],
|
|
233
|
-
initObj: {
|
|
234
|
-
...window["sandbox"].initObj,
|
|
235
|
-
engagementMedium: mode,
|
|
236
|
-
},
|
|
237
|
-
})}
|
|
238
|
-
>
|
|
239
|
-
{mode}
|
|
240
|
-
</a>
|
|
241
|
-
))}
|
|
242
|
-
</details>
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
function UserList(props) {
|
|
246
|
-
return (
|
|
247
|
-
<details
|
|
248
|
-
title={"User: " + window["sandbox"].initObj.user?.firstName}
|
|
249
|
-
key={0}
|
|
250
|
-
id={`dropdown-basic-1`}
|
|
251
|
-
>
|
|
252
|
-
<summary>User</summary>
|
|
253
|
-
{users.map((user, i) => (
|
|
254
|
-
<a
|
|
255
|
-
key={i}
|
|
256
|
-
href={href({
|
|
257
|
-
...window["sandbox"],
|
|
258
|
-
initObj: {
|
|
259
|
-
...window["sandbox"].initObj,
|
|
260
|
-
user: user,
|
|
261
|
-
},
|
|
262
|
-
})}
|
|
263
|
-
>
|
|
264
|
-
{user["firstName"] || "Empty"}
|
|
265
|
-
</a>
|
|
266
|
-
))}
|
|
267
|
-
</details>
|
|
268
|
-
);
|
|
269
|
-
}
|
|
270
|
-
function VersionList(props) {
|
|
271
|
-
const { versions } = props;
|
|
272
|
-
|
|
273
|
-
return (
|
|
274
|
-
<details
|
|
275
|
-
title={"Version: " + window["sandbox"].version || "Head"}
|
|
276
|
-
key={0}
|
|
277
|
-
id={`dropdown-basic-1`}
|
|
278
|
-
>
|
|
279
|
-
<summary>Version</summary>
|
|
280
|
-
{versions.map((v, i) => (
|
|
281
|
-
<a
|
|
282
|
-
key={i}
|
|
283
|
-
href={href({
|
|
284
|
-
...window["sandbox"],
|
|
285
|
-
version: v,
|
|
286
|
-
script:
|
|
287
|
-
v.toLocaleLowerCase() == "head"
|
|
288
|
-
? script
|
|
289
|
-
: v == "local"
|
|
290
|
-
? `./squatchjs.min.js`
|
|
291
|
-
: `https://unpkg.com/@saasquatch/squatch-js@${v}`,
|
|
292
|
-
})}
|
|
293
|
-
>
|
|
294
|
-
<button>{v}</button>
|
|
295
|
-
</a>
|
|
296
|
-
))}
|
|
297
|
-
</details>
|
|
298
|
-
);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
async function getCustomWidget(engagementMedium) {
|
|
302
|
-
window["sandbox"].initObj = {
|
|
303
|
-
...window["sandbox"].initObj,
|
|
304
|
-
engagementMedium,
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
const value = document.getElementById("custom-widget")?.value;
|
|
308
|
-
worker.use(
|
|
309
|
-
rest.put(
|
|
310
|
-
"https://staging.referralsaasquatch.com/api/*",
|
|
311
|
-
(req, res, ctx) => {
|
|
312
|
-
return res(
|
|
313
|
-
ctx.delay(500),
|
|
314
|
-
ctx.status(202, "Mocked status"),
|
|
315
|
-
ctx.json({ jsOptions: {}, user: {}, template: value })
|
|
316
|
-
);
|
|
317
|
-
}
|
|
318
|
-
)
|
|
319
|
-
);
|
|
320
|
-
document.getElementById("squatchembed").innerHTML = "";
|
|
321
|
-
window["squatch"].widgets().upsertUser({
|
|
322
|
-
...window["sandbox"].initObj,
|
|
323
|
-
});
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
function MockedWidgets(props) {
|
|
327
|
-
const { versions } = props;
|
|
328
|
-
const [engagementMedium, setEngagementMedium] = useState("EMBED");
|
|
329
|
-
const [usePreload, setUsePreload] = useState(false);
|
|
330
|
-
const [showWidget, setShowWidget] = useState(false);
|
|
331
|
-
const [widget, setWidget] = useState(undefined);
|
|
332
|
-
const container = usePreload && document.getElementById("squatchembed");
|
|
333
|
-
const [popupTrigger, setPopupTrigger] = useState(".squatchpop");
|
|
334
|
-
|
|
335
|
-
async function getMockWidget(
|
|
336
|
-
widget,
|
|
337
|
-
containerOverride: string | undefined = undefined
|
|
338
|
-
) {
|
|
339
|
-
window["mockWidget"] = widget;
|
|
340
|
-
window["sandbox"].initObj = {
|
|
341
|
-
...window["sandbox"].initObj,
|
|
342
|
-
engagementMedium,
|
|
343
|
-
};
|
|
344
|
-
|
|
345
|
-
worker.use(
|
|
346
|
-
rest.put(
|
|
347
|
-
"https://staging.referralsaasquatch.com/api/*",
|
|
348
|
-
(req, res, ctx) => {
|
|
349
|
-
return res(
|
|
350
|
-
ctx.delay(500),
|
|
351
|
-
ctx.status(202, "Mocked status"),
|
|
352
|
-
ctx.json(widgets[window["mockWidget"]])
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
)
|
|
356
|
-
);
|
|
357
|
-
const defaultElement = document.getElementById(
|
|
358
|
-
"squatchembed"
|
|
359
|
-
) as HTMLElement;
|
|
360
|
-
defaultElement.innerHTML = "";
|
|
361
|
-
document.getElementById("test-selector").innerHTML = "";
|
|
362
|
-
|
|
363
|
-
if (!usePreload) defaultElement.setAttribute("style", "");
|
|
364
|
-
const { widget: embedWidget } = await window["squatch"]
|
|
365
|
-
.widgets()
|
|
366
|
-
.upsertUser({
|
|
367
|
-
...window["sandbox"].initObj,
|
|
368
|
-
container: (usePreload && containerOverride) || container,
|
|
369
|
-
trigger: popupTrigger,
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
if (showWidget) embedWidget.open();
|
|
373
|
-
setWidget(embedWidget);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
return (
|
|
377
|
-
<details
|
|
378
|
-
title={"Version: " + window["sandbox"].version || "Head"}
|
|
379
|
-
key={0}
|
|
380
|
-
id={`dropdown-basic-1`}
|
|
381
|
-
>
|
|
382
|
-
<summary>Mocked Widgets</summary>
|
|
383
|
-
<h4>Engagement Medium</h4>
|
|
384
|
-
<form onSubmit={(e) => e.preventDefault()}>
|
|
385
|
-
<label>Embed</label>
|
|
386
|
-
|
|
387
|
-
<input
|
|
388
|
-
type="radio"
|
|
389
|
-
name="embed"
|
|
390
|
-
checked={engagementMedium === "EMBED"}
|
|
391
|
-
onClick={() => setEngagementMedium("EMBED")}
|
|
392
|
-
></input>
|
|
393
|
-
|
|
394
|
-
<label>Popup</label>
|
|
395
|
-
<input
|
|
396
|
-
type="radio"
|
|
397
|
-
name="popup"
|
|
398
|
-
checked={engagementMedium === "POPUP"}
|
|
399
|
-
onClick={() => setEngagementMedium("POPUP")}
|
|
400
|
-
></input>
|
|
401
|
-
<br />
|
|
402
|
-
<h4>Preload</h4>
|
|
403
|
-
<label>true</label>
|
|
404
|
-
<input
|
|
405
|
-
type="radio"
|
|
406
|
-
name="preload"
|
|
407
|
-
checked={usePreload === true}
|
|
408
|
-
onClick={() => setUsePreload(true)}
|
|
409
|
-
></input>
|
|
410
|
-
|
|
411
|
-
<label>false</label>
|
|
412
|
-
<input
|
|
413
|
-
type="radio"
|
|
414
|
-
name="noPreload"
|
|
415
|
-
checked={usePreload === false}
|
|
416
|
-
onClick={() => setUsePreload(false)}
|
|
417
|
-
></input>
|
|
418
|
-
<br />
|
|
419
|
-
<label>squatch popup trigger</label>
|
|
420
|
-
<input
|
|
421
|
-
value={popupTrigger}
|
|
422
|
-
onChange={(e) => setPopupTrigger(e.target.value)}
|
|
423
|
-
></input>
|
|
424
|
-
</form>
|
|
425
|
-
<br />
|
|
426
|
-
<button
|
|
427
|
-
onClick={() => {
|
|
428
|
-
if (showWidget) {
|
|
429
|
-
setShowWidget(false);
|
|
430
|
-
widget?.close();
|
|
431
|
-
} else {
|
|
432
|
-
setShowWidget(true);
|
|
433
|
-
widget?.open();
|
|
434
|
-
}
|
|
435
|
-
}}
|
|
436
|
-
>
|
|
437
|
-
{showWidget ? "hide widget" : "show widget"}
|
|
438
|
-
</button>
|
|
439
|
-
{engagementMedium === "POPUP" ? (
|
|
440
|
-
<button
|
|
441
|
-
id={popupTrigger.substring(1)}
|
|
442
|
-
className={popupTrigger.substring(1)}
|
|
443
|
-
>
|
|
444
|
-
Open popup
|
|
445
|
-
</button>
|
|
446
|
-
) : (
|
|
447
|
-
""
|
|
448
|
-
)}
|
|
449
|
-
<hr />
|
|
450
|
-
<button onClick={() => getMockWidget("QuirksVanillaGA")}>
|
|
451
|
-
Quirks mode - Vanilla
|
|
452
|
-
</button>
|
|
453
|
-
<button onClick={() => getMockWidget("QuirksMintGA")}>
|
|
454
|
-
Quirks mode - Mint
|
|
455
|
-
</button>
|
|
456
|
-
<button onClick={() => getMockWidget("classic")}>Classic</button>
|
|
457
|
-
<button onClick={() => getMockWidget("MintGA")}>GA - Mint</button>
|
|
458
|
-
<button onClick={() => getMockWidget("VanillaGA")}>GA - Vanilla</button>
|
|
459
|
-
<button onClick={() => getMockWidget("MintGAContainer")}>
|
|
460
|
-
Mint - With Container
|
|
461
|
-
</button>
|
|
462
|
-
<button onClick={() => getMockWidget("QuirksMintGAContainer")}>
|
|
463
|
-
Quirks mode - Mint - With Container
|
|
464
|
-
</button>
|
|
465
|
-
<button onClick={() => getMockWidget("MintGAContainerDisplayBlock")}>
|
|
466
|
-
Mint - With Container + Display Block
|
|
467
|
-
</button>
|
|
468
|
-
<button
|
|
469
|
-
onClick={() => getMockWidget("QuirksMintGAContainerDisplayBlock")}
|
|
470
|
-
>
|
|
471
|
-
Quirks mode - Mint - With Container + Display Block
|
|
472
|
-
</button>
|
|
473
|
-
<button onClick={() => getMockWidget("VanillaGANoContainer")}>
|
|
474
|
-
Vanilla - No Container
|
|
475
|
-
</button>
|
|
476
|
-
<button onClick={() => getMockWidget("MintGA", "#test-selector")}>
|
|
477
|
-
Mint - Selector
|
|
478
|
-
</button>
|
|
479
|
-
<hr />
|
|
480
|
-
</details>
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
function CustomMockedWidget(props) {
|
|
485
|
-
const { versions } = props;
|
|
486
|
-
const [engagementMedium, setEngagementMedium] = useState("EMBED");
|
|
487
|
-
return (
|
|
488
|
-
<details
|
|
489
|
-
title={"Version: " + window["sandbox"].version || "Head"}
|
|
490
|
-
key={0}
|
|
491
|
-
id={`dropdown-basic-1`}
|
|
492
|
-
>
|
|
493
|
-
<summary>Custom Mocked Widget</summary>
|
|
494
|
-
<label>Embed</label>
|
|
495
|
-
<input
|
|
496
|
-
type="radio"
|
|
497
|
-
name="embed"
|
|
498
|
-
checked={engagementMedium === "EMBED"}
|
|
499
|
-
onClick={() => setEngagementMedium("EMBED")}
|
|
500
|
-
></input>
|
|
501
|
-
|
|
502
|
-
<label>Popup</label>
|
|
503
|
-
<input
|
|
504
|
-
type="radio"
|
|
505
|
-
name="popup"
|
|
506
|
-
checked={engagementMedium === "POPUP"}
|
|
507
|
-
onClick={() => setEngagementMedium("POPUP")}
|
|
508
|
-
></input>
|
|
509
|
-
<br />
|
|
510
|
-
<textarea
|
|
511
|
-
id="custom-widget"
|
|
512
|
-
rows={15}
|
|
513
|
-
cols={70}
|
|
514
|
-
style={{ maxWidth: "100%" }}
|
|
515
|
-
></textarea>
|
|
516
|
-
<div>
|
|
517
|
-
<button onClick={() => getCustomWidget(engagementMedium)}>
|
|
518
|
-
Load Widget
|
|
519
|
-
</button>
|
|
520
|
-
</div>
|
|
521
|
-
</details>
|
|
522
|
-
);
|
|
523
|
-
}
|
|
524
|
-
const root = document.getElementById("app");
|
|
525
|
-
|
|
526
|
-
render(<App />, root);
|
|
1
|
+
import React, { Component, useRef, useState, version } from "react";
|
|
2
|
+
import { render } from "react-dom";
|
|
3
|
+
import squatch from "../dist/squatch";
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
popup,
|
|
7
|
+
embed,
|
|
8
|
+
embedNew,
|
|
9
|
+
embedReferred,
|
|
10
|
+
popupNew,
|
|
11
|
+
popupReferred,
|
|
12
|
+
script,
|
|
13
|
+
toURL,
|
|
14
|
+
users,
|
|
15
|
+
href,
|
|
16
|
+
} from "./sandbox";
|
|
17
|
+
import { getVersions } from "./versions";
|
|
18
|
+
import { delay } from "./util";
|
|
19
|
+
import { widgets, worker } from "./generate";
|
|
20
|
+
import { rest } from "msw";
|
|
21
|
+
|
|
22
|
+
// 2. Define request handlers and response resolvers.
|
|
23
|
+
|
|
24
|
+
const modes = ["POPUP", "EMBED"];
|
|
25
|
+
const widgetTypes = [
|
|
26
|
+
"REFERRER_WIDGET",
|
|
27
|
+
"CONVERSION_WIDGET",
|
|
28
|
+
"p/tuesday-test/w/referrerWidget",
|
|
29
|
+
];
|
|
30
|
+
const staticVersions = ["HEAD", "latest", "alpha", "next", "local"];
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Use the addUrlProps higher-order component to hook-in react-url-query.
|
|
34
|
+
*/
|
|
35
|
+
class App extends Component {
|
|
36
|
+
constructor(props) {
|
|
37
|
+
super(props);
|
|
38
|
+
worker.start({
|
|
39
|
+
findWorker: (scriptURL, _mockServiceWorkerUrl) =>
|
|
40
|
+
scriptURL.includes("mockServiceWorker"),
|
|
41
|
+
onUnhandledRequest(req) {
|
|
42
|
+
console.error(
|
|
43
|
+
"Found an unhandled %s request to %s",
|
|
44
|
+
req.method,
|
|
45
|
+
req.url.href
|
|
46
|
+
);
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
state = {
|
|
51
|
+
versions: staticVersions,
|
|
52
|
+
toolbarOpen: true,
|
|
53
|
+
};
|
|
54
|
+
async componentWillMount() {
|
|
55
|
+
const apiVersions = await getVersions();
|
|
56
|
+
const versions = [...staticVersions, ...apiVersions];
|
|
57
|
+
this.setState({ versions });
|
|
58
|
+
}
|
|
59
|
+
render() {
|
|
60
|
+
return (
|
|
61
|
+
<div>
|
|
62
|
+
<button
|
|
63
|
+
onClick={() =>
|
|
64
|
+
this.setState({ toolbarOpen: !this.state.toolbarOpen })
|
|
65
|
+
}
|
|
66
|
+
style={{ float: "right" }}
|
|
67
|
+
>
|
|
68
|
+
{this.state.toolbarOpen ? `<` : `>`}
|
|
69
|
+
</button>
|
|
70
|
+
<div style={{ display: this.state.toolbarOpen ? "block" : "none" }}>
|
|
71
|
+
<hr />
|
|
72
|
+
<div>
|
|
73
|
+
<ParamArea />
|
|
74
|
+
<hr />
|
|
75
|
+
<h2>Quick pick variables</h2>
|
|
76
|
+
<details>
|
|
77
|
+
<summary>Tenant / Program</summary>
|
|
78
|
+
<ul>
|
|
79
|
+
<li>
|
|
80
|
+
<a href={href(popup)}>Popup (classic)</a>
|
|
81
|
+
</li>
|
|
82
|
+
<li>
|
|
83
|
+
<a href={href(embed)}>Embed (classic)</a>
|
|
84
|
+
</li>
|
|
85
|
+
<li>
|
|
86
|
+
<a href={href(popupNew)}>Popup (new program)</a>
|
|
87
|
+
</li>
|
|
88
|
+
<li>
|
|
89
|
+
<a href={href(embedNew)}>Embed (new program)</a>
|
|
90
|
+
</li>
|
|
91
|
+
<li>
|
|
92
|
+
<a href={href(popupReferred)}>
|
|
93
|
+
Popup (classic referred widget)
|
|
94
|
+
</a>
|
|
95
|
+
</li>
|
|
96
|
+
<li>
|
|
97
|
+
<a href={href(embedReferred)}>
|
|
98
|
+
Embed (classic referred widget)
|
|
99
|
+
</a>
|
|
100
|
+
</li>
|
|
101
|
+
</ul>
|
|
102
|
+
</details>
|
|
103
|
+
<WidgetType />
|
|
104
|
+
<ModeList />
|
|
105
|
+
<UserList />
|
|
106
|
+
<VersionList {...this.state} />
|
|
107
|
+
<MockedWidgets />
|
|
108
|
+
<CustomMockedWidget />
|
|
109
|
+
</div>
|
|
110
|
+
<hr />
|
|
111
|
+
|
|
112
|
+
<button onClick={() => recordPurchase()}>Record Purchase</button>
|
|
113
|
+
<hr />
|
|
114
|
+
|
|
115
|
+
<button onClick={() => runEventBomb()}>Event Bomb</button>
|
|
116
|
+
</div>
|
|
117
|
+
</div>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function ParamArea() {
|
|
123
|
+
return (
|
|
124
|
+
<div>
|
|
125
|
+
<h2>Squatch.js Config</h2>
|
|
126
|
+
<div>
|
|
127
|
+
<textarea id="area1" rows={15} cols={70} style={{ maxWidth: "100%" }}>
|
|
128
|
+
{JSON.stringify(window["sandbox"], null, 2)}
|
|
129
|
+
</textarea>
|
|
130
|
+
</div>
|
|
131
|
+
<div>
|
|
132
|
+
<button
|
|
133
|
+
onClick={() => {
|
|
134
|
+
let json: Sandbox = JSON.parse(
|
|
135
|
+
// @ts-ignore
|
|
136
|
+
document.getElementById("area1").value
|
|
137
|
+
);
|
|
138
|
+
toURL(json);
|
|
139
|
+
}}
|
|
140
|
+
>
|
|
141
|
+
Reload Config
|
|
142
|
+
</button>
|
|
143
|
+
<button
|
|
144
|
+
onClick={() => {
|
|
145
|
+
navigator.clipboard.writeText(window.location.toString());
|
|
146
|
+
}}
|
|
147
|
+
>
|
|
148
|
+
Share Current Config
|
|
149
|
+
</button>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
async function recordPurchase() {
|
|
156
|
+
//@ts-ignore
|
|
157
|
+
const { squatch, sandbox } = window;
|
|
158
|
+
const {
|
|
159
|
+
jwt,
|
|
160
|
+
user: { id, accountId },
|
|
161
|
+
} = sandbox.initObj;
|
|
162
|
+
const fields = {
|
|
163
|
+
// Optional
|
|
164
|
+
total: 10.0,
|
|
165
|
+
revenue: 10.0,
|
|
166
|
+
tax: 5.0,
|
|
167
|
+
currency: "USD",
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
await squatch.events().track(
|
|
171
|
+
{
|
|
172
|
+
userId: id,
|
|
173
|
+
accountId: accountId,
|
|
174
|
+
events: [
|
|
175
|
+
{
|
|
176
|
+
key: "purchase",
|
|
177
|
+
fields: fields, // Optional
|
|
178
|
+
// id: "kjv12kbwktb13t3", // Optional id
|
|
179
|
+
// dateTriggered: 1535136384753 // Optional date
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
jwt,
|
|
185
|
+
}
|
|
186
|
+
);
|
|
187
|
+
// TODO: Eventually we'd like an API like this:
|
|
188
|
+
// squatch.events().track("purchase", { ...fields });
|
|
189
|
+
}
|
|
190
|
+
async function runEventBomb() {
|
|
191
|
+
while (true) {
|
|
192
|
+
await recordPurchase();
|
|
193
|
+
await delay(100);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
function WidgetType(props) {
|
|
197
|
+
return (
|
|
198
|
+
<details
|
|
199
|
+
title={window["sandbox"].initObj.widgetType}
|
|
200
|
+
key={0}
|
|
201
|
+
id={`dropdown-basic-1`}
|
|
202
|
+
>
|
|
203
|
+
{widgetTypes.map((widgetType, i) => (
|
|
204
|
+
<a
|
|
205
|
+
key={i}
|
|
206
|
+
href={href({
|
|
207
|
+
...window["sandbox"],
|
|
208
|
+
initObj: {
|
|
209
|
+
...window["sandbox"].initObj,
|
|
210
|
+
widgetType: widgetType,
|
|
211
|
+
},
|
|
212
|
+
})}
|
|
213
|
+
>
|
|
214
|
+
{widgetType}
|
|
215
|
+
</a>
|
|
216
|
+
))}
|
|
217
|
+
</details>
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
function ModeList(props) {
|
|
221
|
+
return (
|
|
222
|
+
<details
|
|
223
|
+
title={window["sandbox"].initObj.engagementMedium}
|
|
224
|
+
key={0}
|
|
225
|
+
id={`dropdown-basic-1`}
|
|
226
|
+
>
|
|
227
|
+
<summary>Engagement Medium</summary>
|
|
228
|
+
{modes.map((mode, i) => (
|
|
229
|
+
<a
|
|
230
|
+
key={i}
|
|
231
|
+
href={href({
|
|
232
|
+
...window["sandbox"],
|
|
233
|
+
initObj: {
|
|
234
|
+
...window["sandbox"].initObj,
|
|
235
|
+
engagementMedium: mode,
|
|
236
|
+
},
|
|
237
|
+
})}
|
|
238
|
+
>
|
|
239
|
+
{mode}
|
|
240
|
+
</a>
|
|
241
|
+
))}
|
|
242
|
+
</details>
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
function UserList(props) {
|
|
246
|
+
return (
|
|
247
|
+
<details
|
|
248
|
+
title={"User: " + window["sandbox"].initObj.user?.firstName}
|
|
249
|
+
key={0}
|
|
250
|
+
id={`dropdown-basic-1`}
|
|
251
|
+
>
|
|
252
|
+
<summary>User</summary>
|
|
253
|
+
{users.map((user, i) => (
|
|
254
|
+
<a
|
|
255
|
+
key={i}
|
|
256
|
+
href={href({
|
|
257
|
+
...window["sandbox"],
|
|
258
|
+
initObj: {
|
|
259
|
+
...window["sandbox"].initObj,
|
|
260
|
+
user: user,
|
|
261
|
+
},
|
|
262
|
+
})}
|
|
263
|
+
>
|
|
264
|
+
{user["firstName"] || "Empty"}
|
|
265
|
+
</a>
|
|
266
|
+
))}
|
|
267
|
+
</details>
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
function VersionList(props) {
|
|
271
|
+
const { versions } = props;
|
|
272
|
+
|
|
273
|
+
return (
|
|
274
|
+
<details
|
|
275
|
+
title={"Version: " + window["sandbox"].version || "Head"}
|
|
276
|
+
key={0}
|
|
277
|
+
id={`dropdown-basic-1`}
|
|
278
|
+
>
|
|
279
|
+
<summary>Version</summary>
|
|
280
|
+
{versions.map((v, i) => (
|
|
281
|
+
<a
|
|
282
|
+
key={i}
|
|
283
|
+
href={href({
|
|
284
|
+
...window["sandbox"],
|
|
285
|
+
version: v,
|
|
286
|
+
script:
|
|
287
|
+
v.toLocaleLowerCase() == "head"
|
|
288
|
+
? script
|
|
289
|
+
: v == "local"
|
|
290
|
+
? `./squatchjs.min.js`
|
|
291
|
+
: `https://unpkg.com/@saasquatch/squatch-js@${v}`,
|
|
292
|
+
})}
|
|
293
|
+
>
|
|
294
|
+
<button>{v}</button>
|
|
295
|
+
</a>
|
|
296
|
+
))}
|
|
297
|
+
</details>
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
async function getCustomWidget(engagementMedium) {
|
|
302
|
+
window["sandbox"].initObj = {
|
|
303
|
+
...window["sandbox"].initObj,
|
|
304
|
+
engagementMedium,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const value = document.getElementById("custom-widget")?.value;
|
|
308
|
+
worker.use(
|
|
309
|
+
rest.put(
|
|
310
|
+
"https://staging.referralsaasquatch.com/api/*",
|
|
311
|
+
(req, res, ctx) => {
|
|
312
|
+
return res(
|
|
313
|
+
ctx.delay(500),
|
|
314
|
+
ctx.status(202, "Mocked status"),
|
|
315
|
+
ctx.json({ jsOptions: {}, user: {}, template: value })
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
)
|
|
319
|
+
);
|
|
320
|
+
document.getElementById("squatchembed").innerHTML = "";
|
|
321
|
+
window["squatch"].widgets().upsertUser({
|
|
322
|
+
...window["sandbox"].initObj,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function MockedWidgets(props) {
|
|
327
|
+
const { versions } = props;
|
|
328
|
+
const [engagementMedium, setEngagementMedium] = useState("EMBED");
|
|
329
|
+
const [usePreload, setUsePreload] = useState(false);
|
|
330
|
+
const [showWidget, setShowWidget] = useState(false);
|
|
331
|
+
const [widget, setWidget] = useState(undefined);
|
|
332
|
+
const container = usePreload && document.getElementById("squatchembed");
|
|
333
|
+
const [popupTrigger, setPopupTrigger] = useState(".squatchpop");
|
|
334
|
+
|
|
335
|
+
async function getMockWidget(
|
|
336
|
+
widget,
|
|
337
|
+
containerOverride: string | undefined = undefined
|
|
338
|
+
) {
|
|
339
|
+
window["mockWidget"] = widget;
|
|
340
|
+
window["sandbox"].initObj = {
|
|
341
|
+
...window["sandbox"].initObj,
|
|
342
|
+
engagementMedium,
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
worker.use(
|
|
346
|
+
rest.put(
|
|
347
|
+
"https://staging.referralsaasquatch.com/api/*",
|
|
348
|
+
(req, res, ctx) => {
|
|
349
|
+
return res(
|
|
350
|
+
ctx.delay(500),
|
|
351
|
+
ctx.status(202, "Mocked status"),
|
|
352
|
+
ctx.json(widgets[window["mockWidget"]])
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
)
|
|
356
|
+
);
|
|
357
|
+
const defaultElement = document.getElementById(
|
|
358
|
+
"squatchembed"
|
|
359
|
+
) as HTMLElement;
|
|
360
|
+
defaultElement.innerHTML = "";
|
|
361
|
+
document.getElementById("test-selector").innerHTML = "";
|
|
362
|
+
|
|
363
|
+
if (!usePreload) defaultElement.setAttribute("style", "");
|
|
364
|
+
const { widget: embedWidget } = await window["squatch"]
|
|
365
|
+
.widgets()
|
|
366
|
+
.upsertUser({
|
|
367
|
+
...window["sandbox"].initObj,
|
|
368
|
+
container: (usePreload && containerOverride) || container,
|
|
369
|
+
trigger: popupTrigger,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
if (showWidget) embedWidget.open();
|
|
373
|
+
setWidget(embedWidget);
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
return (
|
|
377
|
+
<details
|
|
378
|
+
title={"Version: " + window["sandbox"].version || "Head"}
|
|
379
|
+
key={0}
|
|
380
|
+
id={`dropdown-basic-1`}
|
|
381
|
+
>
|
|
382
|
+
<summary>Mocked Widgets</summary>
|
|
383
|
+
<h4>Engagement Medium</h4>
|
|
384
|
+
<form onSubmit={(e) => e.preventDefault()}>
|
|
385
|
+
<label>Embed</label>
|
|
386
|
+
|
|
387
|
+
<input
|
|
388
|
+
type="radio"
|
|
389
|
+
name="embed"
|
|
390
|
+
checked={engagementMedium === "EMBED"}
|
|
391
|
+
onClick={() => setEngagementMedium("EMBED")}
|
|
392
|
+
></input>
|
|
393
|
+
|
|
394
|
+
<label>Popup</label>
|
|
395
|
+
<input
|
|
396
|
+
type="radio"
|
|
397
|
+
name="popup"
|
|
398
|
+
checked={engagementMedium === "POPUP"}
|
|
399
|
+
onClick={() => setEngagementMedium("POPUP")}
|
|
400
|
+
></input>
|
|
401
|
+
<br />
|
|
402
|
+
<h4>Preload</h4>
|
|
403
|
+
<label>true</label>
|
|
404
|
+
<input
|
|
405
|
+
type="radio"
|
|
406
|
+
name="preload"
|
|
407
|
+
checked={usePreload === true}
|
|
408
|
+
onClick={() => setUsePreload(true)}
|
|
409
|
+
></input>
|
|
410
|
+
|
|
411
|
+
<label>false</label>
|
|
412
|
+
<input
|
|
413
|
+
type="radio"
|
|
414
|
+
name="noPreload"
|
|
415
|
+
checked={usePreload === false}
|
|
416
|
+
onClick={() => setUsePreload(false)}
|
|
417
|
+
></input>
|
|
418
|
+
<br />
|
|
419
|
+
<label>squatch popup trigger</label>
|
|
420
|
+
<input
|
|
421
|
+
value={popupTrigger}
|
|
422
|
+
onChange={(e) => setPopupTrigger(e.target.value)}
|
|
423
|
+
></input>
|
|
424
|
+
</form>
|
|
425
|
+
<br />
|
|
426
|
+
<button
|
|
427
|
+
onClick={() => {
|
|
428
|
+
if (showWidget) {
|
|
429
|
+
setShowWidget(false);
|
|
430
|
+
widget?.close();
|
|
431
|
+
} else {
|
|
432
|
+
setShowWidget(true);
|
|
433
|
+
widget?.open();
|
|
434
|
+
}
|
|
435
|
+
}}
|
|
436
|
+
>
|
|
437
|
+
{showWidget ? "hide widget" : "show widget"}
|
|
438
|
+
</button>
|
|
439
|
+
{engagementMedium === "POPUP" ? (
|
|
440
|
+
<button
|
|
441
|
+
id={popupTrigger.substring(1)}
|
|
442
|
+
className={popupTrigger.substring(1)}
|
|
443
|
+
>
|
|
444
|
+
Open popup
|
|
445
|
+
</button>
|
|
446
|
+
) : (
|
|
447
|
+
""
|
|
448
|
+
)}
|
|
449
|
+
<hr />
|
|
450
|
+
<button onClick={() => getMockWidget("QuirksVanillaGA")}>
|
|
451
|
+
Quirks mode - Vanilla
|
|
452
|
+
</button>
|
|
453
|
+
<button onClick={() => getMockWidget("QuirksMintGA")}>
|
|
454
|
+
Quirks mode - Mint
|
|
455
|
+
</button>
|
|
456
|
+
<button onClick={() => getMockWidget("classic")}>Classic</button>
|
|
457
|
+
<button onClick={() => getMockWidget("MintGA")}>GA - Mint</button>
|
|
458
|
+
<button onClick={() => getMockWidget("VanillaGA")}>GA - Vanilla</button>
|
|
459
|
+
<button onClick={() => getMockWidget("MintGAContainer")}>
|
|
460
|
+
Mint - With Container
|
|
461
|
+
</button>
|
|
462
|
+
<button onClick={() => getMockWidget("QuirksMintGAContainer")}>
|
|
463
|
+
Quirks mode - Mint - With Container
|
|
464
|
+
</button>
|
|
465
|
+
<button onClick={() => getMockWidget("MintGAContainerDisplayBlock")}>
|
|
466
|
+
Mint - With Container + Display Block
|
|
467
|
+
</button>
|
|
468
|
+
<button
|
|
469
|
+
onClick={() => getMockWidget("QuirksMintGAContainerDisplayBlock")}
|
|
470
|
+
>
|
|
471
|
+
Quirks mode - Mint - With Container + Display Block
|
|
472
|
+
</button>
|
|
473
|
+
<button onClick={() => getMockWidget("VanillaGANoContainer")}>
|
|
474
|
+
Vanilla - No Container
|
|
475
|
+
</button>
|
|
476
|
+
<button onClick={() => getMockWidget("MintGA", "#test-selector")}>
|
|
477
|
+
Mint - Selector
|
|
478
|
+
</button>
|
|
479
|
+
<hr />
|
|
480
|
+
</details>
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function CustomMockedWidget(props) {
|
|
485
|
+
const { versions } = props;
|
|
486
|
+
const [engagementMedium, setEngagementMedium] = useState("EMBED");
|
|
487
|
+
return (
|
|
488
|
+
<details
|
|
489
|
+
title={"Version: " + window["sandbox"].version || "Head"}
|
|
490
|
+
key={0}
|
|
491
|
+
id={`dropdown-basic-1`}
|
|
492
|
+
>
|
|
493
|
+
<summary>Custom Mocked Widget</summary>
|
|
494
|
+
<label>Embed</label>
|
|
495
|
+
<input
|
|
496
|
+
type="radio"
|
|
497
|
+
name="embed"
|
|
498
|
+
checked={engagementMedium === "EMBED"}
|
|
499
|
+
onClick={() => setEngagementMedium("EMBED")}
|
|
500
|
+
></input>
|
|
501
|
+
|
|
502
|
+
<label>Popup</label>
|
|
503
|
+
<input
|
|
504
|
+
type="radio"
|
|
505
|
+
name="popup"
|
|
506
|
+
checked={engagementMedium === "POPUP"}
|
|
507
|
+
onClick={() => setEngagementMedium("POPUP")}
|
|
508
|
+
></input>
|
|
509
|
+
<br />
|
|
510
|
+
<textarea
|
|
511
|
+
id="custom-widget"
|
|
512
|
+
rows={15}
|
|
513
|
+
cols={70}
|
|
514
|
+
style={{ maxWidth: "100%" }}
|
|
515
|
+
></textarea>
|
|
516
|
+
<div>
|
|
517
|
+
<button onClick={() => getCustomWidget(engagementMedium)}>
|
|
518
|
+
Load Widget
|
|
519
|
+
</button>
|
|
520
|
+
</div>
|
|
521
|
+
</details>
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
const root = document.getElementById("app");
|
|
525
|
+
|
|
526
|
+
render(<App />, root);
|