@netlify/spark-ui 0.0.46-alpha.2 → 0.0.46-alpha.4
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/index.d.ts +2 -1
- package/dist/components/preact/SearchModal/components/SearchModal/SearchModal.d.ts +1 -0
- package/dist/components/preact/SearchModal/components/SearchModal/SearchModal.js +20 -20
- package/dist/components/preact/SearchModal/hooks/useSearchLogger.d.ts +2 -1
- package/dist/components/preact/SearchModal/hooks/useSearchLogger.js +23 -19
- package/package.json +1 -1
- package/packages/analytics/GoogleAnalytics.astro +8 -3
- package/packages/analytics/GoogleTagManager.astro +10 -4
- package/packages/analytics/GoogleTagManagerNoscript.astro +9 -0
- package/packages/analytics/analytics.ts +8 -2
- package/packages/analytics/index.ts +2 -1
- package/packages/components/preact/SearchModal/components/SearchModal/SearchModal.tsx +4 -3
- package/packages/components/preact/SearchModal/hooks/useSearchLogger.ts +32 -15
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { default as GoogleTagManager } from './GoogleTagManager.astro';
|
|
2
|
+
import { default as GoogleTagManagerNoscript } from './GoogleTagManagerNoscript.astro';
|
|
2
3
|
import { default as GoogleAnalytics } from './GoogleAnalytics.astro';
|
|
3
4
|
import { default as Analytics } from './analytics';
|
|
4
|
-
export { Analytics, GoogleAnalytics, GoogleTagManager };
|
|
5
|
+
export { Analytics, GoogleAnalytics, GoogleTagManager, GoogleTagManagerNoscript };
|
|
@@ -1,42 +1,42 @@
|
|
|
1
1
|
import { u as e } from "../../../../../chunks/jsxRuntime.module.Bzuv3cXw.js";
|
|
2
|
-
import { A as v, y as u, d as
|
|
3
|
-
import
|
|
2
|
+
import { A as v, y as u, d as S } from "../../../../../chunks/hooks.module.CNV9mxng.js";
|
|
3
|
+
import L from "../../../Modal/Modal.js";
|
|
4
4
|
import d from "../../../KeyboardKey/KeyboardKey.js";
|
|
5
|
-
import
|
|
6
|
-
import
|
|
7
|
-
import { useSearch as
|
|
8
|
-
import { useSearchLogger as
|
|
5
|
+
import k from "../SearchWindow.js";
|
|
6
|
+
import w from "../SearchInput.js";
|
|
7
|
+
import { useSearch as I } from "../../hooks/useSearch.js";
|
|
8
|
+
import { useSearchLogger as b } from "../../hooks/useSearchLogger.js";
|
|
9
9
|
import { useKeyboardNav as O } from "../../hooks/useKeyboardNav.js";
|
|
10
10
|
import { useSearchHotkeys as P } from "../../hooks/useSearchHotkeys.js";
|
|
11
11
|
import { useSearchParams as E } from "../../hooks/useSearchParams.js";
|
|
12
12
|
import { useSearchToggles as K } from "../../hooks/useSearchToggles.js";
|
|
13
|
-
import '../../../../../assets/SearchModal.css';function M(
|
|
13
|
+
import '../../../../../assets/SearchModal.css';function M(o) {
|
|
14
14
|
const r = v(null);
|
|
15
15
|
return u(() => {
|
|
16
16
|
function t(i) {
|
|
17
|
-
r.current && !r.current.contains(i.target) &&
|
|
17
|
+
r.current && !r.current.contains(i.target) && o();
|
|
18
18
|
}
|
|
19
19
|
return document.addEventListener("mousedown", t), () => {
|
|
20
20
|
document.removeEventListener("mousedown", t);
|
|
21
21
|
};
|
|
22
|
-
}, [
|
|
22
|
+
}, [o]), r;
|
|
23
23
|
}
|
|
24
|
-
function
|
|
25
|
-
const [r, t] =
|
|
24
|
+
function B(o = { className: "", children: null, debug: !1 }) {
|
|
25
|
+
const [r, t] = S(!1);
|
|
26
26
|
u(() => {
|
|
27
27
|
let c;
|
|
28
|
-
const
|
|
28
|
+
const g = () => {
|
|
29
29
|
window.pagefind && (t(!0), c && clearInterval(c));
|
|
30
30
|
};
|
|
31
|
-
return
|
|
31
|
+
return g(), c = setInterval(g, 100), () => clearInterval(c);
|
|
32
32
|
}, []);
|
|
33
|
-
const { children: i, className: f,
|
|
34
|
-
return
|
|
35
|
-
|
|
36
|
-
}, [n]), /* @__PURE__ */ e(
|
|
37
|
-
/* @__PURE__ */ e(
|
|
33
|
+
const { children: i, className: f, debug: N, ...y } = o, { state: a, actions: h } = I(), [n, p] = S(!1), m = () => p(!0), l = () => p(!1), s = v(null), T = M(l);
|
|
34
|
+
return b(a, N), K(r, m), E(r, h.setSearchTerm, m), P(r, n, m, l), O(r, s, n, l), u(() => {
|
|
35
|
+
s.current && (n ? s.current.showModal() : s.current.close());
|
|
36
|
+
}, [n]), /* @__PURE__ */ e(L, { ref: s, className: `n-search-modal ${f || ""}`, ...y, children: /* @__PURE__ */ e("div", { ref: T, className: "n-search-container", children: [
|
|
37
|
+
/* @__PURE__ */ e(w, { setSearchTerm: h.setSearchTerm, initialValue: a.searchTerm }),
|
|
38
38
|
/* @__PURE__ */ e(
|
|
39
|
-
|
|
39
|
+
k,
|
|
40
40
|
{
|
|
41
41
|
searchLoading: a.isLoading,
|
|
42
42
|
searchTerm: a.searchTerm,
|
|
@@ -59,5 +59,5 @@ function z(s = { className: "", children: null }) {
|
|
|
59
59
|
] }) });
|
|
60
60
|
}
|
|
61
61
|
export {
|
|
62
|
-
|
|
62
|
+
B as default
|
|
63
63
|
};
|
|
@@ -12,6 +12,7 @@ import { SearchState } from '../types/search.types';
|
|
|
12
12
|
* @param {SearchState} [state] - The search state, which includes the current search term and the results of the search.
|
|
13
13
|
* - `state.searchTerm`: The current search query string.
|
|
14
14
|
* - `state.results`: The search results array. If it's empty, the hook will log a no-results event.
|
|
15
|
+
* @param {boolean} [debug=false] - Flag to enable debug mode. If true, additional debug information will be sent with the event payloads.
|
|
15
16
|
*
|
|
16
17
|
* @returns {Object} - An object with the following function:
|
|
17
18
|
* - `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.
|
|
@@ -22,6 +23,6 @@ import { SearchState } from '../types/search.types';
|
|
|
22
23
|
* // In your component, attach handleClick to the search result links
|
|
23
24
|
* <a href="result-url" onClick={handleClick}>Search Result</a>
|
|
24
25
|
*/
|
|
25
|
-
export declare function useSearchLogger(state?: SearchState): {
|
|
26
|
+
export declare function useSearchLogger(state?: SearchState, debug?: boolean): {
|
|
26
27
|
handleClick: (e: Event) => void;
|
|
27
28
|
};
|
|
@@ -1,33 +1,37 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
class
|
|
1
|
+
import { A as g, y as f, q as k } from "../../../../chunks/hooks.module.CNV9mxng.js";
|
|
2
|
+
class y {
|
|
3
3
|
/**
|
|
4
4
|
* Pushes an event payload into the dataLayer.
|
|
5
5
|
* @param eventName e.g. 'www:search_query_changed'
|
|
6
6
|
* @param payload additional key/value data
|
|
7
7
|
*/
|
|
8
|
-
track(
|
|
9
|
-
gtag("event",
|
|
8
|
+
track(e, o = {}) {
|
|
9
|
+
typeof gtag == "function" ? gtag("event", e, { ...o }) : console.error("gtag is not defined. Tracking event failed.", e);
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
-
function
|
|
13
|
-
const
|
|
14
|
-
return
|
|
15
|
-
r != null && r.searchTerm
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
function s(r, e) {
|
|
13
|
+
const n = g(new y()).current;
|
|
14
|
+
return f(() => {
|
|
15
|
+
if (!(r != null && r.searchTerm)) return;
|
|
16
|
+
const c = { query: r == null ? void 0 : r.searchTerm };
|
|
17
|
+
e && (c.debug_mode = !0), n.track("spark_search:query_changed", c);
|
|
18
|
+
}, [r == null ? void 0 : r.searchTerm]), f(() => {
|
|
19
|
+
if (!(r != null && r.results)) return;
|
|
20
|
+
const c = { query: r == null ? void 0 : r.searchTerm };
|
|
21
|
+
e && (c.debug_mode = !0), r != null && r.searchTerm && r.results.length === 0 && n.track("spark_search:no_results", c);
|
|
18
22
|
}, [r == null ? void 0 : r.results]), {
|
|
19
|
-
handleClick:
|
|
20
|
-
var
|
|
21
|
-
const l =
|
|
23
|
+
handleClick: k((c) => {
|
|
24
|
+
var i;
|
|
25
|
+
const l = c.target.closest("a");
|
|
22
26
|
if (!l) return;
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
const h = (i = l.querySelector(".n-search-link-label")) == null ? void 0 : i.innerHTML, d = l.getAttribute("href"), u = {
|
|
28
|
+
resultTitle: h,
|
|
29
|
+
resultUrl: d
|
|
30
|
+
};
|
|
31
|
+
e && (u.debug_mode = !0), n.track("spark_search:result_clicked", u);
|
|
28
32
|
}, [])
|
|
29
33
|
};
|
|
30
34
|
}
|
|
31
35
|
export {
|
|
32
|
-
|
|
36
|
+
s as useSearchLogger
|
|
33
37
|
};
|
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.46-alpha.
|
|
4
|
+
"version": "0.0.46-alpha.4",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/components/preact/index.js",
|
|
7
7
|
"types": "dist/components/preact/index.d.ts",
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
const { gaId } = Astro.props;
|
|
3
|
+
|
|
4
|
+
if (!gaId) console.error ('[GoogleTagManager] no GTM id provided')
|
|
5
|
+
---
|
|
1
6
|
<!-- Google tag (gtag.js) -->
|
|
2
|
-
<script async src
|
|
3
|
-
<script>
|
|
7
|
+
<script async src=`https://www.googletagmanager.com/gtag/js?id=${gaId}`></script>
|
|
8
|
+
<script define:vars={{id: gaId}}>
|
|
4
9
|
window.dataLayer = window.dataLayer || [];
|
|
5
10
|
function gtag(){dataLayer.push(arguments);}
|
|
6
11
|
gtag('js', new Date());
|
|
7
12
|
|
|
8
|
-
gtag('config',
|
|
13
|
+
gtag('config', id);
|
|
9
14
|
</script>
|
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
const { gtmId, gaId } = Astro.props;
|
|
3
|
+
|
|
4
|
+
if (!gtmId) console.error ('[GoogleTagManager] no GTM id provided')
|
|
5
|
+
if (!gaId) console.error ('[GoogleTagManager] no GA id provided')
|
|
6
|
+
---
|
|
1
7
|
<!-- Google Tag Manager -->
|
|
2
|
-
<script>
|
|
8
|
+
<script define:vars={{id: gtmId}}>
|
|
3
9
|
(function (w, d, s, l, i) {
|
|
4
10
|
w[l] = w[l] || [];
|
|
5
11
|
w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
|
|
@@ -9,15 +15,15 @@
|
|
|
9
15
|
j.async = true;
|
|
10
16
|
j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl;
|
|
11
17
|
f.parentNode.insertBefore(j, f);
|
|
12
|
-
})(window, document, 'script', 'dataLayer',
|
|
18
|
+
})(window, document, 'script', 'dataLayer', id);
|
|
13
19
|
</script>
|
|
14
20
|
<!-- End Google Tag Manager -->
|
|
15
21
|
|
|
16
22
|
<!-- Google Analytics (gtag.js) -->
|
|
17
|
-
<script>
|
|
23
|
+
<script define:vars={{id: gaId}}>
|
|
18
24
|
function gtag(){dataLayer.push(arguments);}
|
|
19
25
|
gtag('js', new Date());
|
|
20
26
|
|
|
21
|
-
gtag('config',
|
|
27
|
+
gtag('config', id);
|
|
22
28
|
</script>
|
|
23
29
|
<!-- End Google Analytics (gtag.js) -->
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
export default class Analytics {
|
|
2
|
+
|
|
2
3
|
/**
|
|
3
4
|
* Pushes an event payload into the dataLayer.
|
|
4
5
|
* @param eventName e.g. 'www:search_query_changed'
|
|
5
6
|
* @param payload additional key/value data
|
|
6
7
|
*/
|
|
7
8
|
track(eventName: string, payload: Record<string, any> = {}): void {
|
|
8
|
-
gtag
|
|
9
|
+
// Ensures that gtag is available before pushing events
|
|
10
|
+
if (typeof gtag === 'function') {
|
|
11
|
+
gtag('event', eventName, { ...payload });
|
|
12
|
+
} else {
|
|
13
|
+
console.error(`gtag is not defined. Tracking event failed.`, eventName);
|
|
14
|
+
}
|
|
9
15
|
}
|
|
10
|
-
}
|
|
16
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import GoogleTagManager from "./GoogleTagManager.astro";
|
|
2
|
+
import GoogleTagManagerNoscript from "./GoogleTagManagerNoscript.astro";
|
|
2
3
|
import GoogleAnalytics from "./GoogleAnalytics.astro";
|
|
3
4
|
import Analytics from "./analytics"
|
|
4
5
|
|
|
5
|
-
export { Analytics, GoogleAnalytics, GoogleTagManager }
|
|
6
|
+
export { Analytics, GoogleAnalytics, GoogleTagManager, GoogleTagManagerNoscript }
|
|
@@ -20,9 +20,10 @@ import './SearchModal.css';
|
|
|
20
20
|
interface Props {
|
|
21
21
|
children: ComponentChildren;
|
|
22
22
|
className?: string;
|
|
23
|
+
debug?: boolean
|
|
23
24
|
}
|
|
24
25
|
|
|
25
|
-
function SearchModal(props: Props = {className: '', children: null}) {
|
|
26
|
+
function SearchModal(props: Props = {className: '', children: null, debug: false }) {
|
|
26
27
|
const [isPagefindLoaded, setIsPagefindLoaded] = useState(false);
|
|
27
28
|
|
|
28
29
|
// Poll for the existence of window.pagefind.
|
|
@@ -45,7 +46,7 @@ function SearchModal(props: Props = {className: '', children: null}) {
|
|
|
45
46
|
}, []);
|
|
46
47
|
|
|
47
48
|
|
|
48
|
-
const { children, className, ...rest } = props;
|
|
49
|
+
const { children, className, debug, ...rest } = props;
|
|
49
50
|
const { state, actions } = useSearch();
|
|
50
51
|
const [searchOpen, setSearchOpen] = useState(false);
|
|
51
52
|
|
|
@@ -55,7 +56,7 @@ function SearchModal(props: Props = {className: '', children: null}) {
|
|
|
55
56
|
const modalRef = useRef<HTMLDialogElement>(null);
|
|
56
57
|
const containerRef = useClickOutside<HTMLDivElement>(closeSearch);
|
|
57
58
|
|
|
58
|
-
useSearchLogger(state)
|
|
59
|
+
useSearchLogger(state, debug)
|
|
59
60
|
useSearchToggles(isPagefindLoaded, openSearch);
|
|
60
61
|
useSearchParams(isPagefindLoaded, actions.setSearchTerm, openSearch);
|
|
61
62
|
useSearchHotkeys(isPagefindLoaded, searchOpen, openSearch, closeSearch);
|
|
@@ -15,52 +15,69 @@ import Analytics from '../../../../analytics/analytics';
|
|
|
15
15
|
* @param {SearchState} [state] - The search state, which includes the current search term and the results of the search.
|
|
16
16
|
* - `state.searchTerm`: The current search query string.
|
|
17
17
|
* - `state.results`: The search results array. If it's empty, the hook will log a no-results event.
|
|
18
|
-
*
|
|
18
|
+
* @param {boolean} [debug=false] - Flag to enable debug mode. If true, additional debug information will be sent with the event payloads.
|
|
19
|
+
*
|
|
19
20
|
* @returns {Object} - An object with the following function:
|
|
20
21
|
* - `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
|
*
|
|
22
23
|
* @example
|
|
23
24
|
* const { handleClick } = useSearchLogger(searchState);
|
|
24
|
-
*
|
|
25
|
+
*
|
|
25
26
|
* // In your component, attach handleClick to the search result links
|
|
26
27
|
* <a href="result-url" onClick={handleClick}>Search Result</a>
|
|
27
28
|
*/
|
|
28
29
|
|
|
29
|
-
export function useSearchLogger(state?: SearchState) {
|
|
30
|
+
export function useSearchLogger(state?: SearchState, debug?: boolean) {
|
|
30
31
|
const ref = useRef<Analytics>(new Analytics());
|
|
31
32
|
const analytics = ref.current;
|
|
32
33
|
|
|
33
34
|
// Log search query
|
|
34
35
|
useEffect(() => {
|
|
35
|
-
if (!state?.searchTerm) return
|
|
36
|
-
|
|
36
|
+
if (!state?.searchTerm) return;
|
|
37
|
+
const payload = { query: state?.searchTerm };
|
|
38
|
+
|
|
39
|
+
if (debug) {
|
|
40
|
+
payload.debug_mode = true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
analytics.track('spark_search:query_changed', payload);
|
|
37
44
|
}, [state?.searchTerm]);
|
|
38
45
|
|
|
39
46
|
// Log zero‑hit searches
|
|
40
47
|
useEffect(() => {
|
|
41
|
-
if (!state?.results) return
|
|
48
|
+
if (!state?.results) return;
|
|
49
|
+
|
|
50
|
+
const payload = { query: state?.searchTerm };
|
|
51
|
+
|
|
52
|
+
if (debug) {
|
|
53
|
+
payload.debug_mode = true;
|
|
54
|
+
}
|
|
42
55
|
|
|
43
56
|
if (state?.searchTerm && state.results.length === 0) {
|
|
44
|
-
analytics.track('spark_search:no_results',
|
|
57
|
+
analytics.track('spark_search:no_results', payload);
|
|
45
58
|
}
|
|
46
59
|
}, [state?.results]);
|
|
47
60
|
|
|
48
61
|
const handleResultClick = useCallback((e: Event) => {
|
|
49
62
|
const anchor = (e.target as HTMLElement).closest('a');
|
|
50
|
-
|
|
51
|
-
if (!anchor) return
|
|
52
63
|
|
|
53
|
-
|
|
54
|
-
const url = anchor.getAttribute('href')
|
|
64
|
+
if (!anchor) return;
|
|
55
65
|
|
|
56
|
-
|
|
66
|
+
const label = anchor.querySelector('.n-search-link-label')?.innerHTML;
|
|
67
|
+
const url = anchor.getAttribute('href');
|
|
68
|
+
const payload = {
|
|
57
69
|
resultTitle: label,
|
|
58
70
|
resultUrl: url,
|
|
59
|
-
}
|
|
60
|
-
}, []);
|
|
71
|
+
};
|
|
61
72
|
|
|
73
|
+
if (debug) {
|
|
74
|
+
payload.debug_mode = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
analytics.track('spark_search:result_clicked', payload);
|
|
78
|
+
}, []);
|
|
62
79
|
|
|
63
80
|
return {
|
|
64
|
-
handleClick: handleResultClick
|
|
81
|
+
handleClick: handleResultClick,
|
|
65
82
|
};
|
|
66
83
|
}
|