@netlify/spark-ui 0.0.45 → 0.0.46-alpha
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/dist/analytics/analytics.d.ts +8 -0
- package/dist/analytics/index.d.ts +4 -0
- package/dist/components/preact/SearchModal/hooks/useSearchLogger.d.ts +19 -10
- package/dist/components/preact/SearchModal/hooks/useSearchLogger.js +19 -29
- package/package.json +4 -2
- package/packages/analytics/GoogleAnalytics.astro +9 -0
- package/packages/analytics/GoogleTagManager.astro +14 -0
- package/packages/analytics/analytics.ts +10 -0
- package/packages/analytics/index.ts +5 -0
- package/packages/components/preact/SearchModal/hooks/useSearchLogger.ts +27 -18
- package/dist/analytics/logger.d.ts +0 -13
- package/packages/analytics/logger.ts +0 -26
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
import { SearchState } from '../types/search.types';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Custom hook that logs search-related events for analytics.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
5
|
+
* This hook tracks the following search events:
|
|
6
|
+
* - `spark_search:query_changed`: Logs when the search query changes.
|
|
7
|
+
* - `spark_search:no_results`: Logs when no results are found for a search.
|
|
8
|
+
* - `spark_search:result_clicked`: Logs when a search result is clicked.
|
|
9
9
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* It uses an instance of `Analytics` to track these events. The hook listens to changes in the `searchTerm` and `results` properties from the provided `SearchState`.
|
|
11
|
+
*
|
|
12
|
+
* @param {SearchState} [state] - The search state, which includes the current search term and the results of the search.
|
|
13
|
+
* - `state.searchTerm`: The current search query string.
|
|
14
|
+
* - `state.results`: The search results array. If it's empty, the hook will log a no-results event.
|
|
15
|
+
*
|
|
16
|
+
* @returns {Object} - An object with the following function:
|
|
17
|
+
* - `handleClick`: A callback function that should be attached to search result click events. It tracks `spark_search:result_clicked` events with the clicked result's title and URL.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* const { handleClick } = useSearchLogger(searchState);
|
|
21
|
+
*
|
|
22
|
+
* // In your component, attach handleClick to the search result links
|
|
23
|
+
* <a href="result-url" onClick={handleClick}>Search Result</a>
|
|
15
24
|
*/
|
|
16
25
|
export declare function useSearchLogger(state?: SearchState): {
|
|
17
26
|
handleClick: (e: Event) => void;
|
|
@@ -1,43 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
var d = (r, e, c) => e in r ? a(r, e, { enumerable: !0, configurable: !0, writable: !0, value: c }) : r[e] = c;
|
|
3
|
-
var h = (r, e, c) => d(r, typeof e != "symbol" ? e + "" : e, c);
|
|
4
|
-
import { A as L, y as l, q as s } from "../../../../chunks/hooks.module.CNV9mxng.js";
|
|
1
|
+
import { A as k, y as u, q as f } from "../../../../chunks/hooks.module.CNV9mxng.js";
|
|
5
2
|
class g {
|
|
6
|
-
/**
|
|
7
|
-
* @param dl Optional injected dataLayer array. Defaults to window.dataLayer.
|
|
8
|
-
*/
|
|
9
|
-
constructor(e) {
|
|
10
|
-
h(this, "dataLayer");
|
|
11
|
-
this.dataLayer = Array.isArray(e) ? e : window.dataLayer, Array.isArray(this.dataLayer) || (window.dataLayer = [], this.dataLayer = window.dataLayer);
|
|
12
|
-
}
|
|
13
3
|
/**
|
|
14
4
|
* Pushes an event payload into the dataLayer.
|
|
15
|
-
* @param eventName e.g. '
|
|
5
|
+
* @param eventName e.g. 'www:search_query_changed'
|
|
16
6
|
* @param payload additional key/value data
|
|
17
7
|
*/
|
|
18
|
-
|
|
19
|
-
|
|
8
|
+
track(n, c = {}) {
|
|
9
|
+
gtag("event", n, c);
|
|
20
10
|
}
|
|
21
11
|
}
|
|
22
|
-
function
|
|
23
|
-
const
|
|
24
|
-
return
|
|
25
|
-
r != null && r.searchTerm &&
|
|
26
|
-
}, [r == null ? void 0 : r.searchTerm]),
|
|
27
|
-
r != null && r.results && r != null && r.searchTerm && r.results.length === 0 &&
|
|
12
|
+
function T(r) {
|
|
13
|
+
const c = k(new g()).current;
|
|
14
|
+
return u(() => {
|
|
15
|
+
r != null && r.searchTerm && c.track("spark_search:query_changed", { query: r == null ? void 0 : r.searchTerm });
|
|
16
|
+
}, [r == null ? void 0 : r.searchTerm]), u(() => {
|
|
17
|
+
r != null && r.results && r != null && r.searchTerm && r.results.length === 0 && c.track("spark_search:no_results", { query: r == null ? void 0 : r.searchTerm });
|
|
28
18
|
}, [r == null ? void 0 : r.results]), {
|
|
29
|
-
handleClick:
|
|
30
|
-
var
|
|
31
|
-
const
|
|
32
|
-
if (!
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
handleClick: f((h) => {
|
|
20
|
+
var e;
|
|
21
|
+
const l = h.target.closest("a");
|
|
22
|
+
if (!l) return;
|
|
23
|
+
const i = (e = l.querySelector(".n-search-link-label")) == null ? void 0 : e.innerHTML, o = l.getAttribute("href");
|
|
24
|
+
c.track("spark_search:result_clicked", {
|
|
25
|
+
resultTitle: i,
|
|
26
|
+
resultUrl: o
|
|
37
27
|
});
|
|
38
28
|
}, [])
|
|
39
29
|
};
|
|
40
30
|
}
|
|
41
31
|
export {
|
|
42
|
-
|
|
32
|
+
T as useSearchLogger
|
|
43
33
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@netlify/spark-ui",
|
|
3
3
|
"description": "Assets, design tokens, components, and utilities",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.46-alpha",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/components/preact/index.js",
|
|
7
7
|
"types": "dist/components/preact/index.d.ts",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"./logos/*.svg": "./packages/assets/logos/*.svg",
|
|
29
29
|
"./styles/*.css": "./packages/styles/*.css",
|
|
30
30
|
"./components": "./packages/components/index.ts",
|
|
31
|
-
"./utils": "./packages/utils/index.ts"
|
|
31
|
+
"./utils": "./packages/utils/index.ts",
|
|
32
|
+
"./analytics": "./packages/analytics/index.ts"
|
|
32
33
|
},
|
|
33
34
|
"dependencies": {
|
|
34
35
|
"@preact/preset-vite": "^2.10.1",
|
|
@@ -42,6 +43,7 @@
|
|
|
42
43
|
"devDependencies": {
|
|
43
44
|
"@terrazzo/cli": "^0.6.3",
|
|
44
45
|
"@terrazzo/plugin-css": "^0.6.0",
|
|
46
|
+
"@types/gtag.js": "^0.0.20",
|
|
45
47
|
"tslib": "^2.8.1",
|
|
46
48
|
"typescript": "^5.8.2"
|
|
47
49
|
},
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<!-- Google tag (gtag.js) -->
|
|
2
|
+
<script async src="https://www.googletagmanager.com/gtag/js?id=G-X2FMMZSSS9"></script>
|
|
3
|
+
<script>
|
|
4
|
+
window.dataLayer = window.dataLayer || [];
|
|
5
|
+
function gtag(){dataLayer.push(arguments);}
|
|
6
|
+
gtag('js', new Date());
|
|
7
|
+
|
|
8
|
+
gtag('config', 'G-X2FMMZSSS9');
|
|
9
|
+
</script>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!-- Google Tag Manager -->
|
|
2
|
+
<script>
|
|
3
|
+
(function (w, d, s, l, i) {
|
|
4
|
+
w[l] = w[l] || [];
|
|
5
|
+
w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
|
|
6
|
+
var f = d.getElementsByTagName(s)[0],
|
|
7
|
+
j = d.createElement(s),
|
|
8
|
+
dl = l != 'dataLayer' ? '&l=' + l : '';
|
|
9
|
+
j.async = true;
|
|
10
|
+
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
|
|
11
|
+
f.parentNode.insertBefore(j, f);
|
|
12
|
+
})(window, document, 'script', 'dataLayer', 'GTM-T7WNFLD');
|
|
13
|
+
</script>
|
|
14
|
+
<!-- End Google Tag Manager -->
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default class Analytics {
|
|
2
|
+
/**
|
|
3
|
+
* Pushes an event payload into the dataLayer.
|
|
4
|
+
* @param eventName e.g. 'www:search_query_changed'
|
|
5
|
+
* @param payload additional key/value data
|
|
6
|
+
*/
|
|
7
|
+
track(eventName: string, payload: Record<string, any> = {}): void {
|
|
8
|
+
gtag('event', eventName, payload);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
@@ -1,30 +1,39 @@
|
|
|
1
1
|
import type { SearchState } from '../types/search.types';
|
|
2
2
|
import { useEffect, useRef, useCallback } from 'preact/hooks';
|
|
3
|
-
import
|
|
3
|
+
import Analytics from '../../../../analytics/analytics';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
6
|
+
* Custom hook that logs search-related events for analytics.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
* -
|
|
10
|
-
* -
|
|
11
|
-
* -
|
|
8
|
+
* This hook tracks the following search events:
|
|
9
|
+
* - `spark_search:query_changed`: Logs when the search query changes.
|
|
10
|
+
* - `spark_search:no_results`: Logs when no results are found for a search.
|
|
11
|
+
* - `spark_search:result_clicked`: Logs when a search result is clicked.
|
|
12
12
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
13
|
+
* It uses an instance of `Analytics` to track these events. The hook listens to changes in the `searchTerm` and `results` properties from the provided `SearchState`.
|
|
14
|
+
*
|
|
15
|
+
* @param {SearchState} [state] - The search state, which includes the current search term and the results of the search.
|
|
16
|
+
* - `state.searchTerm`: The current search query string.
|
|
17
|
+
* - `state.results`: The search results array. If it's empty, the hook will log a no-results event.
|
|
18
|
+
*
|
|
19
|
+
* @returns {Object} - An object with the following function:
|
|
20
|
+
* - `handleClick`: A callback function that should be attached to search result click events. It tracks `spark_search:result_clicked` events with the clicked result's title and URL.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* const { handleClick } = useSearchLogger(searchState);
|
|
24
|
+
*
|
|
25
|
+
* // In your component, attach handleClick to the search result links
|
|
26
|
+
* <a href="result-url" onClick={handleClick}>Search Result</a>
|
|
18
27
|
*/
|
|
19
28
|
|
|
20
|
-
|
|
21
29
|
export function useSearchLogger(state?: SearchState) {
|
|
22
|
-
const
|
|
30
|
+
const ref = useRef<Analytics>(new Analytics());
|
|
31
|
+
const analytics = ref.current;
|
|
23
32
|
|
|
24
33
|
// Log search query
|
|
25
34
|
useEffect(() => {
|
|
26
35
|
if (!state?.searchTerm) return
|
|
27
|
-
|
|
36
|
+
analytics.track('spark_search:query_changed', { query: state?.searchTerm });
|
|
28
37
|
}, [state?.searchTerm]);
|
|
29
38
|
|
|
30
39
|
// Log zero‑hit searches
|
|
@@ -32,7 +41,7 @@ export function useSearchLogger(state?: SearchState) {
|
|
|
32
41
|
if (!state?.results) return
|
|
33
42
|
|
|
34
43
|
if (state?.searchTerm && state.results.length === 0) {
|
|
35
|
-
|
|
44
|
+
analytics.track('spark_search:no_results', { query: state?.searchTerm });
|
|
36
45
|
}
|
|
37
46
|
}, [state?.results]);
|
|
38
47
|
|
|
@@ -44,9 +53,9 @@ export function useSearchLogger(state?: SearchState) {
|
|
|
44
53
|
const label = anchor.querySelector('.n-search-link-label')?.innerHTML
|
|
45
54
|
const url = anchor.getAttribute('href')
|
|
46
55
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
56
|
+
analytics.track('spark_search:result_clicked', {
|
|
57
|
+
resultTitle: label,
|
|
58
|
+
resultUrl: url,
|
|
50
59
|
});
|
|
51
60
|
}, []);
|
|
52
61
|
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export declare class Logger {
|
|
2
|
-
private dataLayer;
|
|
3
|
-
/**
|
|
4
|
-
* @param dl Optional injected dataLayer array. Defaults to window.dataLayer.
|
|
5
|
-
*/
|
|
6
|
-
constructor(dl?: any[]);
|
|
7
|
-
/**
|
|
8
|
-
* Pushes an event payload into the dataLayer.
|
|
9
|
-
* @param eventName e.g. 'searchResultClick'
|
|
10
|
-
* @param payload additional key/value data
|
|
11
|
-
*/
|
|
12
|
-
push(eventName: string, payload?: Record<string, any>): void;
|
|
13
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
export class Logger {
|
|
2
|
-
private dataLayer: any[];
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* @param dl Optional injected dataLayer array. Defaults to window.dataLayer.
|
|
6
|
-
*/
|
|
7
|
-
constructor(dl?: any[]) {
|
|
8
|
-
// Use injected or global dataLayer
|
|
9
|
-
this.dataLayer = Array.isArray(dl) ? dl : (window as any).dataLayer;
|
|
10
|
-
|
|
11
|
-
// If for some reason it's not an array, initialize it
|
|
12
|
-
if (!Array.isArray(this.dataLayer)) {
|
|
13
|
-
(window as any).dataLayer = [];
|
|
14
|
-
this.dataLayer = (window as any).dataLayer;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Pushes an event payload into the dataLayer.
|
|
20
|
-
* @param eventName e.g. 'searchResultClick'
|
|
21
|
-
* @param payload additional key/value data
|
|
22
|
-
*/
|
|
23
|
-
push(eventName: string, payload: Record<string, any> = {}): void {
|
|
24
|
-
this.dataLayer.push({ event: eventName, ...payload });
|
|
25
|
-
}
|
|
26
|
-
}
|