@reykjavik/webtools 0.1.21 → 0.1.23-canary.1

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 CHANGED
@@ -4,14 +4,21 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
- ## 0.1.21
7
+ ## 0.1.23-canary.1
8
+
9
+ _2024-03-15_
10
+
11
+ - feat: Add framework agnostic `@reykjavik/webtools/SiteImprove` module —
12
+ deprecate `@reykjavik/webtools/next/SiteImprove` instead
13
+
14
+ ## 0.1.21 – 0.1.22
8
15
 
9
16
  _2024-03-14_
10
17
 
11
18
  - feat: Add `@reykjavik/webtools/remix/Wait` component
12
19
  - feat: Add `@reykjavik/webtools/remix/http` module — with `isClientFetch`
13
20
  helper
14
- - feat: Add `@reykjavik/webtools/remix/async` module with promise helpers
21
+ - feat: Add `@reykjavik/webtools/async` module with promise helpers
15
22
 
16
23
  ## 0.1.18 – 0.1.20
17
24
 
package/README.md CHANGED
@@ -22,20 +22,24 @@ bun add @reykjavik/webtools
22
22
  - [`cacheControl` helper](#cachecontrol-helper)
23
23
  - [Type `TTLConfig`](#type-ttlconfig)
24
24
  - [`toSec` TTL helper](#tosec-ttl-helper)
25
- - [`@reykjavik/webtools/CookieHubConsent`](#reykjavikwebtoolscookiehubconsent)
26
- - [`CookieHubProvider` component](#cookiehubprovider-component)
27
- - [`useCookieHubConsent`](#usecookiehubconsent)
28
25
  - [`@reykjavik/webtools/async`](#reykjavikwebtoolsasync)
29
26
  - [`promiseAllObject`](#promiseallobject)
30
27
  - [`maxWait`](#maxwait)
28
+ - [`@reykjavik/webtools/fixIcelandicLocale`](#reykjavikwebtoolsfixicelandiclocale)
29
+ - [Limitations](#limitations)
30
+ - [`@reykjavik/webtools/SiteImprove`](#reykjavikwebtoolssiteimprove)
31
+ - [`SiteImprove` component](#siteimprove-component)
32
+ - [`pingSiteImprove` helper](#pingsiteimprove-helper)
33
+ - [`pingSiteImproveOutbound` helper](#pingsiteimproveoutbound-helper)
34
+ - [`@reykjavik/webtools/CookieHubConsent`](#reykjavikwebtoolscookiehubconsent)
35
+ - [`CookieHubProvider` component](#cookiehubprovider-component)
36
+ - [`useCookieHubConsent`](#usecookiehubconsent)
31
37
  - [`@reykjavik/webtools/vanillaExtract`](#reykjavikwebtoolsvanillaextract)
32
38
  - [`vanillaGlobal`](#vanillaglobal)
33
39
  - [`vanillaProps`](#vanillaprops)
34
40
  - [`vanillaClass`](#vanillaclass)
35
41
  - [`vanillaClassNested`](#vanillaclassnested)
36
42
  - [`vanillaNest`](#vanillanest)
37
- - [`@reykjavik/webtools/fixIcelandicLocale`](#reykjavikwebtoolsfixicelandiclocale)
38
- - [Limitations](#limitations)
39
43
  - [Framework Specific Tools](#framework-specific-tools)
40
44
  - [Next.js Tools](#nextjs-tools)
41
45
  - [Remix.run Tools](#remixrun-tools)
@@ -184,6 +188,216 @@ const ttlSec = toSec(ttl);
184
188
 
185
189
  ---
186
190
 
191
+ ## `@reykjavik/webtools/async`
192
+
193
+ Contains a few small helpers for working with async functions and promises.
194
+
195
+ ---
196
+
197
+ ### `promiseAllObject`
198
+
199
+ **Syntax:**
200
+ `promiseAllObject<T extends PlainObj>(promisesMap: T>): Promise<{ [K in keyof T]: Awaited<T[K]>; }>`
201
+
202
+ A variation of `Promise.all()` that accepts an object with named promises and
203
+ returns a same-shaped object with the resolved values.
204
+
205
+ ```ts
206
+ import { promiseAllObject } from '@reykjavik/webtools/async';
207
+
208
+ const { user, posts } = await promiseAllObject({
209
+ user: fetchUser(),
210
+ posts: fetchPosts(),
211
+ });
212
+ ```
213
+
214
+ ---
215
+
216
+ ### `maxWait`
217
+
218
+ **Syntax:** `maxWait(timeout: number, promises: Array<any>): Promise<void>`
219
+ **Syntax:**
220
+ `maxWait<T extends PlainObj>(timeout: number, promises: T): Promise<{ [K in keyof T]: { value: Awaited<T[K]> } | undefined }>`
221
+
222
+ This somewhat esoteric helper resolves soon as all of the passed `promises`
223
+ have resolved, or after `timeout` milliseconds — whichever comes first.
224
+
225
+ If an object is passed, the resolved value will be an object with the same
226
+ keys, with undefined values for any promises that didn't resolve in time, and
227
+ the resolved values in a `value` container object.
228
+
229
+ ```ts
230
+ import { maxWait } from '@reykjavik/webtools/async';
231
+
232
+ const user = fetchUser();
233
+ const posts = fetchPosts();
234
+
235
+ // Array of promises resolves to void
236
+ await maxWait(500, [user, posts]);
237
+
238
+ // Object of promises resolves to an object with any resolved values at that time
239
+ const { user, posts } = await maxWait(500, { user, posts });
240
+
241
+ console.log(user?.value); // undefined | User
242
+ console.log(posts?.value); // undefined | Array<Post>
243
+ ```
244
+
245
+ ---
246
+
247
+ ## `@reykjavik/webtools/fixIcelandicLocale`
248
+
249
+ As of early 2004, Google Chrome still does not support the Icelandic locale
250
+ `is`/`is-IS` in any way. Meanwhile other browsers have supported it for over a
251
+ decade.
252
+
253
+ This module does attempts to patches the following methods/classes by
254
+ substituting the `is` locale with `da` (Danish) and apply a few post-hoc fixes
255
+ to their return values.
256
+
257
+ - `Intl.Collator` and `String.prototype.localeCompare`
258
+ - `Intl.NumberFormat` and `Number.prototype.toLocaleString`
259
+ - `Intl.DateTimeFormat` and `Date.prototype.toLocaleDateString`
260
+ - `Intl.PluralRules`
261
+ - `Intl.ListFormat`
262
+
263
+ This provides usable (but not perfect) results, with some caveats listed
264
+ below.
265
+
266
+ To apply these patches, simply "side-effect import" this module at the top of
267
+ your app's entry point:
268
+
269
+ ```ts
270
+ import '@reykjavik/webtools/fixIcelandicLocale';
271
+
272
+ // Then continue with your day and use `localeCompare` and other Intl.* methods
273
+ // as you normally would. (See "limitations" below.)
274
+ ```
275
+
276
+ (**NOTE** The patch is only applied in engines that fail a simple feature
277
+ detection test.)
278
+
279
+ ### Limitations
280
+
281
+ **`Intl.Collator` and `localeCompare`:**
282
+
283
+ - It incorrectly treats `ð` and `d` as the same letter (most of the time), and
284
+ the acute-accented characters `á`, `é`, `í`, `ó`, `ú` and `ý` get lumped in
285
+ with their non-accented counterparts (unless the compared).
286
+ We fix this only for the first letter in the string, but not for the rest of
287
+ it.
288
+
289
+ **`Intl.NumberFormat` and `toLocaleString`:**
290
+
291
+ - The `style: "unit"` option is not supported and prints units in Danish. (Soo
292
+ many units and unit-variants…)
293
+ - The `currencyDisplay: "name"` option is not supported and prints the
294
+ currency's full name in Danish.
295
+
296
+ **`Intl.DateTimeFormat` and `toLocaleDateString`:**
297
+
298
+ - The `month: 'narrow'` and `weekday: 'narrow'` options are not supported, and
299
+ print the corresponding Danish initials.
300
+ - For `timeZoneName` the values `"long"`, `"shortGeneric"` and `"longGeneric"`
301
+ will appear in Danish.
302
+ - The `timeStyle: 'full'` option prints the timezone names in Danish
303
+ - The `dayPeriod` option has a couple of slight mismatches, at 5 am and 12
304
+ noon.
305
+
306
+ ---
307
+
308
+ ## `@reykjavik/webtools/SiteImprove`
309
+
310
+ Contains React helpers for loading SiteImprove's analytics scripts, and
311
+ perform page-view and custom event tracking in applications with client-side
312
+ (`pushState`) routing.
313
+
314
+ ### `SiteImprove` component
315
+
316
+ A component for loading a SiteImprove analytics script and set up page-view
317
+ tracking across URL routes.
318
+
319
+ It also automatically logs all out-bound link clicks, to match the behavior of
320
+ the vanilla SiteImprove script.
321
+
322
+ **Props:**
323
+
324
+ The Component's props have detailed JSDoc comments (displayed in your code
325
+ editor), but there's a brief summary:
326
+
327
+ - `accountId?: string` — Your SiteImprove account ID. (alternative to
328
+ `scriptUrl` prop).
329
+ - `scriptUrl?: string` — The full SiteImprove analytics script URL.
330
+ (alternative to `accountId` prop).
331
+ - `hasConsented?: boolean` — Manual GDPR 'analytics' consent flag. Allows hard
332
+ opt-out, but defers to
333
+ [`CookieHubProvider` values](./README.md#usecookiehubconsent) if they are
334
+ available.
335
+ - `onLoad?: (e: unknown) => void` — Fires when the script has loaded.
336
+ - `onError?: (e: unknown) => void` — Fires if loading the script failed.
337
+
338
+ Example usage somewhere in your application:
339
+
340
+ ```js
341
+ import { SiteImprove } from '@reykjavik/webtools/SiteImprove';
342
+
343
+ // ideally emit this from your loader function
344
+ const siteImproveAccountId = '[ACCOUNT_ID]'; // e.g. "7654321"
345
+
346
+ const location = useRouter()
347
+ // ...then Inside root.tsx component:
348
+ <SiteImprove
349
+ accountId={siteImproveAccountId}
350
+ onError={(error) =>
351
+ Logger('error', 'An error occured initializing siteimprove', error)
352
+ }
353
+ />;
354
+ ```
355
+
356
+ In dev mode it does NOT load the SiteImprove script and merely logs page-view
357
+ events to the console.
358
+
359
+ ### `pingSiteImprove` helper
360
+
361
+ **Syntax:**
362
+ `pingSiteImprove(category: string, action: string, label?: string): void`
363
+
364
+ A small helper for tracking custom UI events and reporting them to SiteImrove.
365
+
366
+ It safely manages GDPR consent, so you can use it unconditionally.
367
+
368
+ ```js
369
+ import { pingSiteImprove } from '@reykjavik/webtools/SiteImprove';
370
+
371
+ const handleSubmit = () => {
372
+ // perform submit action...
373
+ if (success) {
374
+ pingSiteImprove('application', 'add_new');
375
+ }
376
+ };
377
+ ```
378
+
379
+ ### `pingSiteImproveOutbound` helper
380
+
381
+ **Syntax:** `pingSiteImproveOutbound(ourl: string): void`
382
+
383
+ A small helper for reporting to SiteImrove when the user is programmatically
384
+ being sent to a different URL/resource.
385
+
386
+ ```js
387
+ import { pingSiteImproveOutbound } from '@reykjavik/webtools/SiteImprove';
388
+
389
+ const handleSubmit = () => {
390
+ // perform submit action...
391
+ if (success) {
392
+ const fileUrl = '/download/report.pdf';
393
+ pingSiteImproveOutbound(fileUrl);
394
+ document.location.href = fileUrl;
395
+ }
396
+ };
397
+ ```
398
+
399
+ ---
400
+
187
401
  ## `@reykjavik/webtools/CookieHubConsent`
188
402
 
189
403
  Contains React helpers for loading CookieHub's consent manager and reading
@@ -257,62 +471,6 @@ this hook will return an empty object.
257
471
 
258
472
  ---
259
473
 
260
- ## `@reykjavik/webtools/async`
261
-
262
- Contains a few small helpers for working with async functions and promises.
263
-
264
- ---
265
-
266
- ### `promiseAllObject`
267
-
268
- **Syntax:**
269
- `promiseAllObject<T extends PlainObj>(promisesMap: T>): Promise<{ [K in keyof T]: Awaited<T[K]>; }>`
270
-
271
- A variation of `Promise.all()` that accepts an object with named promises and
272
- returns a same-shaped object with the resolved values.
273
-
274
- ```ts
275
- import { promiseAllObject } from '@reykjavik/webtools/async';
276
-
277
- const { user, posts } = await promiseAllObject({
278
- user: fetchUser(),
279
- posts: fetchPosts(),
280
- });
281
- ```
282
-
283
- ---
284
-
285
- ### `maxWait`
286
-
287
- **Syntax:** `maxWait(timeout: number, promises: Array<any>): Promise<void>`
288
- **Syntax:**
289
- `maxWait<T extends PlainObj>(timeout: number, promises: T): Promise<{ [K in keyof T]: { value: Awaited<T[K]> } | undefined }>`
290
-
291
- This somewhat esoteric helper resolves soon as all of the passed `promises`
292
- have resolved, or after `timeout` milliseconds — whichever comes first.
293
-
294
- If an object is passed, the resolved value will be an object with the same
295
- keys, with undefined values for any promises that didn't resolve in time, and
296
- the resolved values in a `value` container object.
297
-
298
- ```ts
299
- import { maxWait } from '@reykjavik/webtools/async';
300
-
301
- const user = fetchUser();
302
- const posts = fetchPosts();
303
-
304
- // Array of promises resolves to void
305
- await maxWait(500, [user, posts]);
306
-
307
- // Object of promises resolves to an object with any resolved values at that time
308
- const { user, posts } = await maxWait(500, { user, posts });
309
-
310
- console.log(user?.value); // undefined | User
311
- console.log(posts?.value); // undefined | Array<Post>
312
- ```
313
-
314
- ---
315
-
316
474
  ## `@reykjavik/webtools/vanillaExtract`
317
475
 
318
476
  Contains helpers for writing [vanilla-extract](https://vanilla-extract.style)
@@ -486,67 +644,6 @@ vanillaGlobal(`
486
644
 
487
645
  ---
488
646
 
489
- ## `@reykjavik/webtools/fixIcelandicLocale`
490
-
491
- As of early 2004, Google Chrome still does not support the Icelandic locale
492
- `is`/`is-IS` in any way. Meanwhile other browsers have supported it for over a
493
- decade.
494
-
495
- This module does attempts to patches the following methods/classes by
496
- substituting the `is` locale with `da` (Danish) and apply a few post-hoc fixes
497
- to their return values.
498
-
499
- - `Intl.Collator` and `String.prototype.localeCompare`
500
- - `Intl.NumberFormat` and `Number.prototype.toLocaleString`
501
- - `Intl.DateTimeFormat` and `Date.prototype.toLocaleDateString`
502
- - `Intl.PluralRules`
503
- - `Intl.ListFormat`
504
-
505
- This provides usable (but not perfect) results, with some caveats listed
506
- below.
507
-
508
- To apply these patches, simply "side-effect import" this module at the top of
509
- your app's entry point:
510
-
511
- ```ts
512
- import '@reykjavik/webtools/fixIcelandicLocale';
513
-
514
- // Then continue with your day and use `localeCompare` and other Intl.* methods
515
- // as you normally would. (See "limitations" below.)
516
- ```
517
-
518
- (**NOTE** The patch is only applied in engines that fail a simple feature
519
- detection test.)
520
-
521
- ### Limitations
522
-
523
- **`Intl.Collator` and `localeCompare`:**
524
-
525
- - It incorrectly treats `ð` and `d` as the same letter (most of the time), and
526
- the acute-accented characters `á`, `é`, `í`, `ó`, `ú` and `ý` get lumped in
527
- with their non-accented counterparts (unless the compared).
528
- We fix this only for the first letter in the string, but not for the rest of
529
- it.
530
-
531
- **`Intl.NumberFormat` and `toLocaleString`:**
532
-
533
- - The `style: "unit"` option is not supported and prints units in Danish. (Soo
534
- many units and unit-variants…)
535
- - The `currencyDisplay: "name"` option is not supported and prints the
536
- currency's full name in Danish.
537
-
538
- **`Intl.DateTimeFormat` and `toLocaleDateString`:**
539
-
540
- - The `month: 'narrow'` and `weekday: 'narrow'` options are not supported, and
541
- print the corresponding Danish initials.
542
- - For `timeZoneName` the values `"long"`, `"shortGeneric"` and `"longGeneric"`
543
- will appear in Danish.
544
- - The `timeStyle: 'full'` option prints the timezone names in Danish
545
- - The `dayPeriod` option has a couple of slight mismatches, at 5 am and 12
546
- noon.
547
-
548
- ---
549
-
550
647
  ## Framework Specific Tools
551
648
 
552
649
  ---
@@ -556,8 +653,6 @@ detection test.)
556
653
  <a name="reykjavikwebtoolsnexthttp"></a> <a name="makeerrorizeapphoc"></a>
557
654
  <a name="showerrorpage-helper"></a> <a name="notmodified304-helper"></a>
558
655
  <a name="reykjavikwebtoolsnextsiteimprove"></a>
559
- <a name="siteimprove-component"></a> <a name="pingsiteimprove-helper"></a>
560
- <a name="pingsiteimproveoutbound-helper"></a>
561
656
 
562
657
  ### Next.js Tools
563
658
 
@@ -1,3 +1,4 @@
1
+ import React from 'react';
1
2
  import { EitherObj } from '@reykjavik/hanna-utils';
2
3
  declare global {
3
4
  interface Window {
@@ -14,8 +15,8 @@ declare global {
14
15
  };
15
16
  }
16
17
  }
17
- export type SiteImproveEvent = SiteImprovePageView | SiteImproveRequest | SiteImproveCustomEvent;
18
- export type SiteImprovePageView = [
18
+ type SiteImproveEvent = SiteImprovePageView | SiteImproveRequest | SiteImproveCustomEvent;
19
+ type SiteImprovePageView = [
19
20
  type: 'trackdynamic',
20
21
  data: {
21
22
  /** New page URL */
@@ -26,7 +27,7 @@ export type SiteImprovePageView = [
26
27
  title?: string;
27
28
  }
28
29
  ];
29
- export type SiteImproveRequest = [
30
+ type SiteImproveRequest = [
30
31
  type: 'request',
31
32
  data: {
32
33
  /** Outbound URL */
@@ -36,33 +37,25 @@ export type SiteImproveRequest = [
36
37
  autoonclick?: 1;
37
38
  }
38
39
  ];
39
- export type SiteImproveCustomEvent = [
40
+ type SiteImproveCustomEvent = [
40
41
  type: 'event',
41
42
  category: string,
42
43
  action: string,
43
44
  label?: string
44
45
  ];
45
- /**
46
- * A small helper to send "trackdynamic" page view/load events to SiteImrove.
47
- *
48
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
49
- */
50
- export declare const trackDynamicPageView: (url: string, refUrl?: string, title?: string) => void;
51
46
  /**
52
47
  * A small helper for tracking custom UI events and reporting them to SiteImrove.
53
48
  *
54
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
49
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimprove-helper
55
50
  */
56
51
  export declare const pingSiteImprove: (category: string, action: string, label?: string) => void;
57
52
  /**
58
53
  * A small helper for reporting to SiteImrove when the user is programmatically
59
54
  * being sent to a different URL/resource.
60
55
  *
61
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimproveoutbound-helper
56
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimproveoutbound-helper
62
57
  */
63
58
  export declare const pingSiteImproveOutbound: (ourl: string) => void;
64
- export declare const logOutboundLinks: () => () => void;
65
- export declare const makeScriptUrl: (accountId: string) => string;
66
59
  export type SiteImproveProps = EitherObj<{
67
60
  /**
68
61
  * Your SiteImprove account ID.
@@ -88,7 +81,7 @@ export type SiteImproveProps = EitherObj<{
88
81
  * A value of `true` still defers to the 'analytics' consent state provided
89
82
  * by the `CookieHubProvider` component (if present).
90
83
  */
91
- hasConstented?: boolean;
84
+ hasConsented?: boolean;
92
85
  /**
93
86
  * Custom callback for when the SiteImprove script has loaded.
94
87
  */
@@ -98,3 +91,11 @@ export type SiteImproveProps = EitherObj<{
98
91
  */
99
92
  onError?: (e: unknown) => void;
100
93
  };
94
+ /**
95
+ * A component for loading a SiteImprove analytics script and set up page-view
96
+ * tracking across client-side (pushState, replaceState) routing.
97
+ *
98
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#siteimprove-component
99
+ */
100
+ export declare const SiteImprove: (props: SiteImproveProps) => React.JSX.Element | null;
101
+ export {};
package/SiteImprove.js ADDED
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.SiteImprove = exports.pingSiteImproveOutbound = exports.pingSiteImprove = void 0;
27
+ const react_1 = __importStar(require("react"));
28
+ const CookieHubConsent_js_1 = require("./CookieHubConsent.js");
29
+ // END: Mock typing of SiteImprove's event tracking API
30
+ // --------------------------------------------------------------------------
31
+ //
32
+ // ---------------------------------------------------------------------------
33
+ const _emitEvent = typeof window === 'undefined'
34
+ ? () => undefined
35
+ : (event) => {
36
+ let _sz = window._sz;
37
+ if (!_sz) {
38
+ _sz = window._sz = [];
39
+ _sz._jit_defined_ = true;
40
+ }
41
+ _sz.push(event);
42
+ if (process.env.NODE_ENV === 'development') {
43
+ console.info('SiteImprove:', event);
44
+ }
45
+ };
46
+ // ---------------------------------------------------------------------------
47
+ /**
48
+ * A small helper to send "trackdynamic" page view/load events to SiteImrove.
49
+ */
50
+ const trackDynamicPageView = (url, refUrl, title) => _emitEvent(['trackdynamic', { url, ref: refUrl, title }]);
51
+ // ---------------------------------------------------------------------------
52
+ /**
53
+ * A small helper for tracking custom UI events and reporting them to SiteImrove.
54
+ *
55
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimprove-helper
56
+ */
57
+ const pingSiteImprove = (category, action, label) => {
58
+ if (process.env.NODE_ENV === 'development' &&
59
+ (!window._sz || window._sz._jit_defined_)) {
60
+ console.warn('`pingSiteImprove` was called before SiteImprove script was loaded.');
61
+ }
62
+ _emitEvent(['event', category, action, label]);
63
+ };
64
+ exports.pingSiteImprove = pingSiteImprove;
65
+ // ---------------------------------------------------------------------------
66
+ /**
67
+ * A small helper for reporting to SiteImrove when the user is programmatically
68
+ * being sent to a different URL/resource.
69
+ *
70
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimproveoutbound-helper
71
+ */
72
+ const pingSiteImproveOutbound = (ourl) => {
73
+ if (process.env.NODE_ENV === 'development' &&
74
+ (!window._sz || window._sz._jit_defined_)) {
75
+ console.warn('`pingSiteImproveOutbound` was called before SiteImprove script was loaded.');
76
+ }
77
+ _emitEvent(['request', { ourl, ref: document.location.href }]);
78
+ };
79
+ exports.pingSiteImproveOutbound = pingSiteImproveOutbound;
80
+ // ---------------------------------------------------------------------------
81
+ const logOutboundLinks = () => {
82
+ const captureLinkClicks = (e) => {
83
+ const link = e.target.closest('a[href]');
84
+ if (!link || link.$$bound) {
85
+ return;
86
+ }
87
+ link.$$bound = true;
88
+ // Waiting for the bubble phase allows other click handlers to preventDefault()
89
+ link.addEventListener('click', (e) => {
90
+ var _a, _b;
91
+ if (e.defaultPrevented) {
92
+ return;
93
+ }
94
+ // Skip logging outbound request if SiteImprove has already done so.
95
+ // BTW, SiteImprove binds its autoonclick handlers on "mousedown"
96
+ // so they're guaranteed to have run before our "click" listener.
97
+ const events = (_b = (_a = window._sz) === null || _a === void 0 ? void 0 : _a.core) === null || _b === void 0 ? void 0 : _b.data;
98
+ const [type, data] = (events && events[events.length - 1]) || [];
99
+ if (type === 'request' && data.autoonclick && data.ourl === link.href) {
100
+ return;
101
+ }
102
+ (0, exports.pingSiteImproveOutbound)(link.href);
103
+ });
104
+ };
105
+ const { body } = document;
106
+ // bind 'click' listener to the capture phase
107
+ body.addEventListener('click', captureLinkClicks, true);
108
+ return () => body.removeEventListener('click', captureLinkClicks, true);
109
+ };
110
+ // ---------------------------------------------------------------------------
111
+ const makeScriptUrl = (accountId) => `https://siteimproveanalytics.com/js/siteanalyze_${accountId}.js`;
112
+ // ---------------------------------------------------------------------------
113
+ const useResolvedAnalyticsConsent = (hasConsented) => {
114
+ const { analytics } = (0, CookieHubConsent_js_1.useCookieHubConsent)();
115
+ return ((analytics && hasConsented !== false) || (analytics === undefined && hasConsented));
116
+ };
117
+ // ---------------------------------------------------------------------------
118
+ const mockSIGlobalIfNeeded = (props) => {
119
+ if (process.env.NODE_ENV !== 'production') {
120
+ console.info('Mock loading SiteImprove in development mode.', props.scriptUrl || props.accountId);
121
+ if (!window._sz) {
122
+ setTimeout(() => {
123
+ window._sz = window._sz || [];
124
+ }, 300);
125
+ }
126
+ }
127
+ };
128
+ // ===========================================================================
129
+ let lastUrl = '';
130
+ /**
131
+ * A component for loading a SiteImprove analytics script and set up page-view
132
+ * tracking across client-side (pushState, replaceState) routing.
133
+ *
134
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#siteimprove-component
135
+ */
136
+ const SiteImprove = (props) => {
137
+ var _a;
138
+ const consented = useResolvedAnalyticsConsent(props.hasConsented);
139
+ const { pathname, search, hash } = typeof document !== 'undefined'
140
+ ? document.location
141
+ : { pathname: '.', search: '', hash: '' };
142
+ const thisUrl = pathname + search + hash;
143
+ (0, react_1.useEffect)(() => {
144
+ if (consented && lastUrl && thisUrl !== lastUrl) {
145
+ trackDynamicPageView(thisUrl, lastUrl, document.title);
146
+ lastUrl = thisUrl;
147
+ }
148
+ }, [consented, thisUrl]);
149
+ (0, react_1.useEffect)(() => {
150
+ if (!consented) {
151
+ return;
152
+ }
153
+ mockSIGlobalIfNeeded(props);
154
+ return logOutboundLinks();
155
+ },
156
+ // eslint-disable-next-line react-hooks/exhaustive-deps
157
+ [consented]);
158
+ if (!consented || process.env.NODE_ENV !== 'production') {
159
+ return null;
160
+ }
161
+ const scriptUrl = (_a = props.scriptUrl) !== null && _a !== void 0 ? _a : makeScriptUrl(props.accountId);
162
+ return react_1.default.createElement("script", { defer: true, src: scriptUrl, onLoad: props.onLoad, onError: props.onError });
163
+ };
164
+ exports.SiteImprove = SiteImprove;
@@ -1,3 +1,4 @@
1
+ import React from 'react';
1
2
  import { EitherObj } from '@reykjavik/hanna-utils';
2
3
  declare global {
3
4
  interface Window {
@@ -14,8 +15,8 @@ declare global {
14
15
  };
15
16
  }
16
17
  }
17
- export type SiteImproveEvent = SiteImprovePageView | SiteImproveRequest | SiteImproveCustomEvent;
18
- export type SiteImprovePageView = [
18
+ type SiteImproveEvent = SiteImprovePageView | SiteImproveRequest | SiteImproveCustomEvent;
19
+ type SiteImprovePageView = [
19
20
  type: 'trackdynamic',
20
21
  data: {
21
22
  /** New page URL */
@@ -26,7 +27,7 @@ export type SiteImprovePageView = [
26
27
  title?: string;
27
28
  }
28
29
  ];
29
- export type SiteImproveRequest = [
30
+ type SiteImproveRequest = [
30
31
  type: 'request',
31
32
  data: {
32
33
  /** Outbound URL */
@@ -36,33 +37,25 @@ export type SiteImproveRequest = [
36
37
  autoonclick?: 1;
37
38
  }
38
39
  ];
39
- export type SiteImproveCustomEvent = [
40
+ type SiteImproveCustomEvent = [
40
41
  type: 'event',
41
42
  category: string,
42
43
  action: string,
43
44
  label?: string
44
45
  ];
45
- /**
46
- * A small helper to send "trackdynamic" page view/load events to SiteImrove.
47
- *
48
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
49
- */
50
- export declare const trackDynamicPageView: (url: string, refUrl?: string, title?: string) => void;
51
46
  /**
52
47
  * A small helper for tracking custom UI events and reporting them to SiteImrove.
53
48
  *
54
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
49
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimprove-helper
55
50
  */
56
51
  export declare const pingSiteImprove: (category: string, action: string, label?: string) => void;
57
52
  /**
58
53
  * A small helper for reporting to SiteImrove when the user is programmatically
59
54
  * being sent to a different URL/resource.
60
55
  *
61
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimproveoutbound-helper
56
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimproveoutbound-helper
62
57
  */
63
58
  export declare const pingSiteImproveOutbound: (ourl: string) => void;
64
- export declare const logOutboundLinks: () => () => void;
65
- export declare const makeScriptUrl: (accountId: string) => string;
66
59
  export type SiteImproveProps = EitherObj<{
67
60
  /**
68
61
  * Your SiteImprove account ID.
@@ -88,7 +81,7 @@ export type SiteImproveProps = EitherObj<{
88
81
  * A value of `true` still defers to the 'analytics' consent state provided
89
82
  * by the `CookieHubProvider` component (if present).
90
83
  */
91
- hasConstented?: boolean;
84
+ hasConsented?: boolean;
92
85
  /**
93
86
  * Custom callback for when the SiteImprove script has loaded.
94
87
  */
@@ -98,3 +91,11 @@ export type SiteImproveProps = EitherObj<{
98
91
  */
99
92
  onError?: (e: unknown) => void;
100
93
  };
94
+ /**
95
+ * A component for loading a SiteImprove analytics script and set up page-view
96
+ * tracking across client-side (pushState, replaceState) routing.
97
+ *
98
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#siteimprove-component
99
+ */
100
+ export declare const SiteImprove: (props: SiteImproveProps) => React.JSX.Element | null;
101
+ export {};
@@ -1,6 +1,5 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.makeScriptUrl = exports.logOutboundLinks = exports.pingSiteImproveOutbound = exports.pingSiteImprove = exports.trackDynamicPageView = void 0;
1
+ import React, { useEffect } from 'react';
2
+ import { useCookieHubConsent } from './CookieHubConsent.js';
4
3
  // END: Mock typing of SiteImprove's event tracking API
5
4
  // --------------------------------------------------------------------------
6
5
  //
@@ -21,40 +20,35 @@ const _emitEvent = typeof window === 'undefined'
21
20
  // ---------------------------------------------------------------------------
22
21
  /**
23
22
  * A small helper to send "trackdynamic" page view/load events to SiteImrove.
24
- *
25
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
26
23
  */
27
24
  const trackDynamicPageView = (url, refUrl, title) => _emitEvent(['trackdynamic', { url, ref: refUrl, title }]);
28
- exports.trackDynamicPageView = trackDynamicPageView;
29
25
  // ---------------------------------------------------------------------------
30
26
  /**
31
27
  * A small helper for tracking custom UI events and reporting them to SiteImrove.
32
28
  *
33
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
29
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimprove-helper
34
30
  */
35
- const pingSiteImprove = (category, action, label) => {
31
+ export const pingSiteImprove = (category, action, label) => {
36
32
  if (process.env.NODE_ENV === 'development' &&
37
33
  (!window._sz || window._sz._jit_defined_)) {
38
34
  console.warn('`pingSiteImprove` was called before SiteImprove script was loaded.');
39
35
  }
40
36
  _emitEvent(['event', category, action, label]);
41
37
  };
42
- exports.pingSiteImprove = pingSiteImprove;
43
38
  // ---------------------------------------------------------------------------
44
39
  /**
45
40
  * A small helper for reporting to SiteImrove when the user is programmatically
46
41
  * being sent to a different URL/resource.
47
42
  *
48
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimproveoutbound-helper
43
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#pingsiteimproveoutbound-helper
49
44
  */
50
- const pingSiteImproveOutbound = (ourl) => {
45
+ export const pingSiteImproveOutbound = (ourl) => {
51
46
  if (process.env.NODE_ENV === 'development' &&
52
47
  (!window._sz || window._sz._jit_defined_)) {
53
48
  console.warn('`pingSiteImproveOutbound` was called before SiteImprove script was loaded.');
54
49
  }
55
50
  _emitEvent(['request', { ourl, ref: document.location.href }]);
56
51
  };
57
- exports.pingSiteImproveOutbound = pingSiteImproveOutbound;
58
52
  // ---------------------------------------------------------------------------
59
53
  const logOutboundLinks = () => {
60
54
  const captureLinkClicks = (e) => {
@@ -77,7 +71,7 @@ const logOutboundLinks = () => {
77
71
  if (type === 'request' && data.autoonclick && data.ourl === link.href) {
78
72
  return;
79
73
  }
80
- (0, exports.pingSiteImproveOutbound)(link.href);
74
+ pingSiteImproveOutbound(link.href);
81
75
  });
82
76
  };
83
77
  const { body } = document;
@@ -85,7 +79,57 @@ const logOutboundLinks = () => {
85
79
  body.addEventListener('click', captureLinkClicks, true);
86
80
  return () => body.removeEventListener('click', captureLinkClicks, true);
87
81
  };
88
- exports.logOutboundLinks = logOutboundLinks;
89
82
  // ---------------------------------------------------------------------------
90
83
  const makeScriptUrl = (accountId) => `https://siteimproveanalytics.com/js/siteanalyze_${accountId}.js`;
91
- exports.makeScriptUrl = makeScriptUrl;
84
+ // ---------------------------------------------------------------------------
85
+ const useResolvedAnalyticsConsent = (hasConsented) => {
86
+ const { analytics } = useCookieHubConsent();
87
+ return ((analytics && hasConsented !== false) || (analytics === undefined && hasConsented));
88
+ };
89
+ // ---------------------------------------------------------------------------
90
+ const mockSIGlobalIfNeeded = (props) => {
91
+ if (process.env.NODE_ENV !== 'production') {
92
+ console.info('Mock loading SiteImprove in development mode.', props.scriptUrl || props.accountId);
93
+ if (!window._sz) {
94
+ setTimeout(() => {
95
+ window._sz = window._sz || [];
96
+ }, 300);
97
+ }
98
+ }
99
+ };
100
+ // ===========================================================================
101
+ let lastUrl = '';
102
+ /**
103
+ * A component for loading a SiteImprove analytics script and set up page-view
104
+ * tracking across client-side (pushState, replaceState) routing.
105
+ *
106
+ * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README.md#siteimprove-component
107
+ */
108
+ export const SiteImprove = (props) => {
109
+ var _a;
110
+ const consented = useResolvedAnalyticsConsent(props.hasConsented);
111
+ const { pathname, search, hash } = typeof document !== 'undefined'
112
+ ? document.location
113
+ : { pathname: '.', search: '', hash: '' };
114
+ const thisUrl = pathname + search + hash;
115
+ useEffect(() => {
116
+ if (consented && lastUrl && thisUrl !== lastUrl) {
117
+ trackDynamicPageView(thisUrl, lastUrl, document.title);
118
+ lastUrl = thisUrl;
119
+ }
120
+ }, [consented, thisUrl]);
121
+ useEffect(() => {
122
+ if (!consented) {
123
+ return;
124
+ }
125
+ mockSIGlobalIfNeeded(props);
126
+ return logOutboundLinks();
127
+ },
128
+ // eslint-disable-next-line react-hooks/exhaustive-deps
129
+ [consented]);
130
+ if (!consented || process.env.NODE_ENV !== 'production') {
131
+ return null;
132
+ }
133
+ const scriptUrl = (_a = props.scriptUrl) !== null && _a !== void 0 ? _a : makeScriptUrl(props.accountId);
134
+ return React.createElement("script", { defer: true, src: scriptUrl, onLoad: props.onLoad, onError: props.onError });
135
+ };
package/esm/index.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  /// <reference path="./async.d.ts" />
4
4
  /// <reference path="./fixIcelandicLocale.d.ts" />
5
5
  /// <reference path="./remix/http.d.ts" />
6
+ /// <reference path="./SiteImprove.d.tsx" />
6
7
  /// <reference path="./CookieHubConsent.d.tsx" />
7
8
  /// <reference path="./remix/Wait.d.tsx" />
8
9
  /// <reference path="./next/SiteImprove.d.tsx" />
@@ -1,11 +1,10 @@
1
- import React from 'react';
2
- import { SiteImproveProps } from './SiteImprove.privates.js';
3
- export type { SiteImproveProps } from './SiteImprove.privates.js';
4
- export { pingSiteImprove, pingSiteImproveOutbound } from './SiteImprove.privates.js';
5
- /**
6
- * A component for loading a SiteImprove analytics script and set up page-view
7
- * tracking across Next.js routes.
8
- *
9
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#siteimprove-component
10
- */
11
- export declare const SiteImprove: (props: SiteImproveProps) => React.JSX.Element | null;
1
+ /// <reference types="react" resolution-mode="require"/>
2
+ import { SiteImproveProps as _SiteImproveProps } from '../SiteImprove.js';
3
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
4
+ export type SiteImproveProps = _SiteImproveProps;
5
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
6
+ export declare const SiteImprove: (props: _SiteImproveProps) => import("react").JSX.Element | null;
7
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
8
+ export declare const pingSiteImprove: (category: string, action: string, label?: string | undefined) => void;
9
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
10
+ export declare const pingSiteImproveOutbound: (ourl: string) => void;
@@ -1,75 +1,7 @@
1
- import React, { useEffect } from 'react';
2
- import { Router } from 'next/router.js';
3
- import NextScript from 'next/script.js';
4
- import { useCookieHubConsent } from '../CookieHubConsent.js';
5
- import { logOutboundLinks, makeScriptUrl, trackDynamicPageView, } from './SiteImprove.privates.js';
6
- // Fixes an issue with `next/script`'s types and mixture of default and named exports.
7
- // This workaround doesn't seem to be necessary in Next.js 13.5 (pages router), but
8
- // is definitely needed for the webpack bundler used by Next.js 11. (v12 is untested.)
9
- const Script = ('__esModule' in NextScript && 'default' in NextScript
10
- ? NextScript.default
11
- : NextScript);
12
- // ---------------------------------------------------------------------------
13
- /*
14
- SiteImprove's "trackdynamic" (page view) event requires both the new URL
15
- and the old (referer) URL.
16
- Next router's `routeChangeComplete` (which is the correct point in time
17
- to send the tracking event) does not provide access to the previous URL,
18
- so we need to capture it during `routeChangeStart`.
19
- We feel it's safe to assume that every `routeChangeComplete` event
20
- always fires directly after its `routeChangeStart` counterpart,
21
- and it is thus safe to simply store the old URL in a local variable.
22
- This may look dodgy, but should prove reasonably safe in practice.
23
- */
24
- let _refUrl = '';
25
- const captureRefUrl = () => {
26
- _refUrl = document.location.pathname + document.location.search;
27
- };
28
- const sendRoutingEvent = (url) => {
29
- // On `history.back()`/`history.forward()` the URL change happens before
30
- // `routeChangeStart`, so `refUrl` and `url` become the same.
31
- // in that case we suppress the `ref`
32
- const refUrl = _refUrl !== url ? _refUrl : undefined;
33
- trackDynamicPageView(url, refUrl, document.title);
34
- };
35
- export { pingSiteImprove, pingSiteImproveOutbound } from './SiteImprove.privates.js';
36
- /**
37
- * A component for loading a SiteImprove analytics script and set up page-view
38
- * tracking across Next.js routes.
39
- *
40
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#siteimprove-component
41
- */
42
- export const SiteImprove = (props) => {
43
- const { analytics } = useCookieHubConsent();
44
- const consented = (analytics && props.hasConstented !== false) ||
45
- (analytics === undefined && props.hasConstented);
46
- useEffect(() => {
47
- if (!consented) {
48
- return;
49
- }
50
- if (process.env.NODE_ENV !== 'production') {
51
- console.info('Mock loading SiteImprove in development mode.', props.scriptUrl || props.accountId);
52
- if (!window._sz) {
53
- setTimeout(() => {
54
- window._sz = window._sz || [];
55
- }, 300);
56
- }
57
- }
58
- const routerEvents = Router.events;
59
- routerEvents.on('routeChangeStart', captureRefUrl);
60
- routerEvents.on('routeChangeComplete', sendRoutingEvent);
61
- const stopLoggingOutboundLinks = logOutboundLinks();
62
- return () => {
63
- routerEvents.off('routeChangeStart', captureRefUrl);
64
- routerEvents.off('routeChangeComplete', sendRoutingEvent);
65
- stopLoggingOutboundLinks();
66
- };
67
- },
68
- // eslint-disable-next-line react-hooks/exhaustive-deps
69
- [consented]);
70
- if (!consented || process.env.NODE_ENV !== 'production') {
71
- return null;
72
- }
73
- const scriptUrl = props.scriptUrl != null ? props.scriptUrl : makeScriptUrl(props.accountId);
74
- return (React.createElement(Script, { type: "text/javascript", strategy: "afterInteractive", src: scriptUrl, onLoad: props.onLoad, onError: props.onError }));
75
- };
1
+ import { pingSiteImprove as _pingSiteImprove, pingSiteImproveOutbound as _pingSiteImproveOutbound, SiteImprove as _SiteImprove, } from '../SiteImprove.js';
2
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
3
+ export const SiteImprove = _SiteImprove;
4
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
5
+ export const pingSiteImprove = _pingSiteImprove;
6
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
7
+ export const pingSiteImproveOutbound = _pingSiteImproveOutbound;
@@ -35,7 +35,7 @@ export declare const makeErrorizeAppHOC: <EP extends Partial<ErrorProps>>(ErrorP
35
35
  }>(App: AppType<P>): React.FunctionComponent<import("next/dist/shared/lib/utils.js").AppPropsType<any, P | ({
36
36
  __error: ErrorProps;
37
37
  } & Omit<ErrorProps & EP, keyof ErrorProps>)>> & {
38
- getInitialProps?(context: import("next/dist/shared/lib/utils.js").AppContextType<import("next/router.js").NextRouter>): P | ({
38
+ getInitialProps?(context: import("next/dist/shared/lib/utils.js").AppContextType<import("next/dist/client/router.js").NextRouter>): P | ({
39
39
  __error: ErrorProps;
40
40
  } & Omit<ErrorProps & EP, keyof ErrorProps>) | Promise<P | ({
41
41
  __error: ErrorProps;
@@ -18,12 +18,12 @@ type WaitFallbacks = {
18
18
  /**
19
19
  * Custom loading/spinner component.
20
20
  */
21
- meanwhile?: Exclude<ReactNode, number>;
21
+ meanwhile?: ReactNode;
22
22
  /**
23
23
  * Custom error component if the promise is rejected or if it resolves to an
24
24
  * object with an `error` property.
25
25
  */
26
- error?: Exclude<ReactNode, number>;
26
+ error?: ReactNode;
27
27
  };
28
28
  export type WaitProps<T> = WaitPropsBase<T> & WaitFallbacks;
29
29
  /**
package/index.d.ts CHANGED
@@ -3,6 +3,7 @@
3
3
  /// <reference path="./async.d.ts" />
4
4
  /// <reference path="./fixIcelandicLocale.d.ts" />
5
5
  /// <reference path="./remix/http.d.ts" />
6
+ /// <reference path="./SiteImprove.d.tsx" />
6
7
  /// <reference path="./CookieHubConsent.d.tsx" />
7
8
  /// <reference path="./remix/Wait.d.tsx" />
8
9
  /// <reference path="./next/SiteImprove.d.tsx" />
@@ -1,11 +1,10 @@
1
- import React from 'react';
2
- import { SiteImproveProps } from './SiteImprove.privates.js';
3
- export type { SiteImproveProps } from './SiteImprove.privates.js';
4
- export { pingSiteImprove, pingSiteImproveOutbound } from './SiteImprove.privates.js';
5
- /**
6
- * A component for loading a SiteImprove analytics script and set up page-view
7
- * tracking across Next.js routes.
8
- *
9
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#siteimprove-component
10
- */
11
- export declare const SiteImprove: (props: SiteImproveProps) => React.JSX.Element | null;
1
+ /// <reference types="react" />
2
+ import { SiteImproveProps as _SiteImproveProps } from '../SiteImprove.js';
3
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
4
+ export type SiteImproveProps = _SiteImproveProps;
5
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
6
+ export declare const SiteImprove: (props: _SiteImproveProps) => import("react").JSX.Element | null;
7
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
8
+ export declare const pingSiteImprove: (category: string, action: string, label?: string | undefined) => void;
9
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
10
+ export declare const pingSiteImproveOutbound: (ourl: string) => void;
@@ -1,107 +1,10 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
24
- };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
27
- };
28
2
  Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.SiteImprove = exports.pingSiteImproveOutbound = exports.pingSiteImprove = void 0;
30
- const react_1 = __importStar(require("react"));
31
- const router_js_1 = require("next/router.js");
32
- const script_js_1 = __importDefault(require("next/script.js"));
33
- const CookieHubConsent_js_1 = require("../CookieHubConsent.js");
34
- const SiteImprove_privates_js_1 = require("./SiteImprove.privates.js");
35
- // Fixes an issue with `next/script`'s types and mixture of default and named exports.
36
- // This workaround doesn't seem to be necessary in Next.js 13.5 (pages router), but
37
- // is definitely needed for the webpack bundler used by Next.js 11. (v12 is untested.)
38
- const Script = ('__esModule' in script_js_1.default && 'default' in script_js_1.default
39
- ? script_js_1.default.default
40
- : script_js_1.default);
41
- // ---------------------------------------------------------------------------
42
- /*
43
- SiteImprove's "trackdynamic" (page view) event requires both the new URL
44
- and the old (referer) URL.
45
- Next router's `routeChangeComplete` (which is the correct point in time
46
- to send the tracking event) does not provide access to the previous URL,
47
- so we need to capture it during `routeChangeStart`.
48
- We feel it's safe to assume that every `routeChangeComplete` event
49
- always fires directly after its `routeChangeStart` counterpart,
50
- and it is thus safe to simply store the old URL in a local variable.
51
- This may look dodgy, but should prove reasonably safe in practice.
52
- */
53
- let _refUrl = '';
54
- const captureRefUrl = () => {
55
- _refUrl = document.location.pathname + document.location.search;
56
- };
57
- const sendRoutingEvent = (url) => {
58
- // On `history.back()`/`history.forward()` the URL change happens before
59
- // `routeChangeStart`, so `refUrl` and `url` become the same.
60
- // in that case we suppress the `ref`
61
- const refUrl = _refUrl !== url ? _refUrl : undefined;
62
- (0, SiteImprove_privates_js_1.trackDynamicPageView)(url, refUrl, document.title);
63
- };
64
- var SiteImprove_privates_js_2 = require("./SiteImprove.privates.js");
65
- Object.defineProperty(exports, "pingSiteImprove", { enumerable: true, get: function () { return SiteImprove_privates_js_2.pingSiteImprove; } });
66
- Object.defineProperty(exports, "pingSiteImproveOutbound", { enumerable: true, get: function () { return SiteImprove_privates_js_2.pingSiteImproveOutbound; } });
67
- /**
68
- * A component for loading a SiteImprove analytics script and set up page-view
69
- * tracking across Next.js routes.
70
- *
71
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#siteimprove-component
72
- */
73
- const SiteImprove = (props) => {
74
- const { analytics } = (0, CookieHubConsent_js_1.useCookieHubConsent)();
75
- const consented = (analytics && props.hasConstented !== false) ||
76
- (analytics === undefined && props.hasConstented);
77
- (0, react_1.useEffect)(() => {
78
- if (!consented) {
79
- return;
80
- }
81
- if (process.env.NODE_ENV !== 'production') {
82
- console.info('Mock loading SiteImprove in development mode.', props.scriptUrl || props.accountId);
83
- if (!window._sz) {
84
- setTimeout(() => {
85
- window._sz = window._sz || [];
86
- }, 300);
87
- }
88
- }
89
- const routerEvents = router_js_1.Router.events;
90
- routerEvents.on('routeChangeStart', captureRefUrl);
91
- routerEvents.on('routeChangeComplete', sendRoutingEvent);
92
- const stopLoggingOutboundLinks = (0, SiteImprove_privates_js_1.logOutboundLinks)();
93
- return () => {
94
- routerEvents.off('routeChangeStart', captureRefUrl);
95
- routerEvents.off('routeChangeComplete', sendRoutingEvent);
96
- stopLoggingOutboundLinks();
97
- };
98
- },
99
- // eslint-disable-next-line react-hooks/exhaustive-deps
100
- [consented]);
101
- if (!consented || process.env.NODE_ENV !== 'production') {
102
- return null;
103
- }
104
- const scriptUrl = props.scriptUrl != null ? props.scriptUrl : (0, SiteImprove_privates_js_1.makeScriptUrl)(props.accountId);
105
- return (react_1.default.createElement(Script, { type: "text/javascript", strategy: "afterInteractive", src: scriptUrl, onLoad: props.onLoad, onError: props.onError }));
106
- };
107
- exports.SiteImprove = SiteImprove;
3
+ exports.pingSiteImproveOutbound = exports.pingSiteImprove = exports.SiteImprove = void 0;
4
+ const SiteImprove_js_1 = require("../SiteImprove.js");
5
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
6
+ exports.SiteImprove = SiteImprove_js_1.SiteImprove;
7
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
8
+ exports.pingSiteImprove = SiteImprove_js_1.pingSiteImprove;
9
+ /** @deprecated Instead import from `@reykjavik/webtools/SiteImprove` (Will be removed in v0.2) */
10
+ exports.pingSiteImproveOutbound = SiteImprove_js_1.pingSiteImproveOutbound;
package/next/http.d.ts CHANGED
@@ -35,7 +35,7 @@ export declare const makeErrorizeAppHOC: <EP extends Partial<ErrorProps>>(ErrorP
35
35
  }>(App: AppType<P>): React.FunctionComponent<import("next/dist/shared/lib/utils.js").AppPropsType<any, P | ({
36
36
  __error: ErrorProps;
37
37
  } & Omit<ErrorProps & EP, keyof ErrorProps>)>> & {
38
- getInitialProps?(context: import("next/dist/shared/lib/utils.js").AppContextType<import("next/router.js").NextRouter>): P | ({
38
+ getInitialProps?(context: import("next/dist/shared/lib/utils.js").AppContextType<import("next/dist/client/router.js").NextRouter>): P | ({
39
39
  __error: ErrorProps;
40
40
  } & Omit<ErrorProps & EP, keyof ErrorProps>) | Promise<P | ({
41
41
  __error: ErrorProps;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/webtools",
3
- "version": "0.1.21",
3
+ "version": "0.1.23-canary.1",
4
4
  "description": "Misc. JS/TS helpers used by Reykjavík City's web dev teams.",
5
5
  "main": "index.js",
6
6
  "repository": "ssh://git@github.com:reykjavikcity/webtools.git",
@@ -68,6 +68,10 @@
68
68
  "import": "./esm/remix/http.js",
69
69
  "require": "./remix/http.js"
70
70
  },
71
+ "./SiteImprove": {
72
+ "import": "./esm/SiteImprove.js",
73
+ "require": "./SiteImprove.js"
74
+ },
71
75
  "./CookieHubConsent": {
72
76
  "import": "./esm/CookieHubConsent.js",
73
77
  "require": "./CookieHubConsent.js"
package/remix/Wait.d.ts CHANGED
@@ -18,12 +18,12 @@ type WaitFallbacks = {
18
18
  /**
19
19
  * Custom loading/spinner component.
20
20
  */
21
- meanwhile?: Exclude<ReactNode, number>;
21
+ meanwhile?: ReactNode;
22
22
  /**
23
23
  * Custom error component if the promise is rejected or if it resolves to an
24
24
  * object with an `error` property.
25
25
  */
26
- error?: Exclude<ReactNode, number>;
26
+ error?: ReactNode;
27
27
  };
28
28
  export type WaitProps<T> = WaitPropsBase<T> & WaitFallbacks;
29
29
  /**
@@ -1,83 +0,0 @@
1
- // END: Mock typing of SiteImprove's event tracking API
2
- // --------------------------------------------------------------------------
3
- //
4
- // ---------------------------------------------------------------------------
5
- const _emitEvent = typeof window === 'undefined'
6
- ? () => undefined
7
- : (event) => {
8
- let _sz = window._sz;
9
- if (!_sz) {
10
- _sz = window._sz = [];
11
- _sz._jit_defined_ = true;
12
- }
13
- _sz.push(event);
14
- if (process.env.NODE_ENV === 'development') {
15
- console.info('SiteImprove:', event);
16
- }
17
- };
18
- // ---------------------------------------------------------------------------
19
- /**
20
- * A small helper to send "trackdynamic" page view/load events to SiteImrove.
21
- *
22
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
23
- */
24
- export const trackDynamicPageView = (url, refUrl, title) => _emitEvent(['trackdynamic', { url, ref: refUrl, title }]);
25
- // ---------------------------------------------------------------------------
26
- /**
27
- * A small helper for tracking custom UI events and reporting them to SiteImrove.
28
- *
29
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimprove-helper
30
- */
31
- export const pingSiteImprove = (category, action, label) => {
32
- if (process.env.NODE_ENV === 'development' &&
33
- (!window._sz || window._sz._jit_defined_)) {
34
- console.warn('`pingSiteImprove` was called before SiteImprove script was loaded.');
35
- }
36
- _emitEvent(['event', category, action, label]);
37
- };
38
- // ---------------------------------------------------------------------------
39
- /**
40
- * A small helper for reporting to SiteImrove when the user is programmatically
41
- * being sent to a different URL/resource.
42
- *
43
- * @see https://github.com/reykjavikcity/webtools/blob/v0.1/README-nextjs.md#pingsiteimproveoutbound-helper
44
- */
45
- export const pingSiteImproveOutbound = (ourl) => {
46
- if (process.env.NODE_ENV === 'development' &&
47
- (!window._sz || window._sz._jit_defined_)) {
48
- console.warn('`pingSiteImproveOutbound` was called before SiteImprove script was loaded.');
49
- }
50
- _emitEvent(['request', { ourl, ref: document.location.href }]);
51
- };
52
- // ---------------------------------------------------------------------------
53
- export const logOutboundLinks = () => {
54
- const captureLinkClicks = (e) => {
55
- const link = e.target.closest('a[href]');
56
- if (!link || link.$$bound) {
57
- return;
58
- }
59
- link.$$bound = true;
60
- // Waiting for the bubble phase allows other click handlers to preventDefault()
61
- link.addEventListener('click', (e) => {
62
- var _a, _b;
63
- if (e.defaultPrevented) {
64
- return;
65
- }
66
- // Skip logging outbound request if SiteImprove has already done so.
67
- // BTW, SiteImprove binds its autoonclick handlers on "mousedown"
68
- // so they're guaranteed to have run before our "click" listener.
69
- const events = (_b = (_a = window._sz) === null || _a === void 0 ? void 0 : _a.core) === null || _b === void 0 ? void 0 : _b.data;
70
- const [type, data] = (events && events[events.length - 1]) || [];
71
- if (type === 'request' && data.autoonclick && data.ourl === link.href) {
72
- return;
73
- }
74
- pingSiteImproveOutbound(link.href);
75
- });
76
- };
77
- const { body } = document;
78
- // bind 'click' listener to the capture phase
79
- body.addEventListener('click', captureLinkClicks, true);
80
- return () => body.removeEventListener('click', captureLinkClicks, true);
81
- };
82
- // ---------------------------------------------------------------------------
83
- export const makeScriptUrl = (accountId) => `https://siteimproveanalytics.com/js/siteanalyze_${accountId}.js`;