@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.
@@ -0,0 +1,8 @@
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
+ }
@@ -0,0 +1,4 @@
1
+ import { default as GoogleTagManager } from './GoogleTagManager.astro';
2
+ import { default as GoogleAnalytics } from './GoogleAnalytics.astro';
3
+ import { default as Analytics } from './analytics';
4
+ export { Analytics, GoogleAnalytics, GoogleTagManager };
@@ -1,17 +1,26 @@
1
1
  import { SearchState } from '../types/search.types';
2
2
  /**
3
- * Hook that logs search-related events to your analytics Logger.
3
+ * Custom hook that logs search-related events for analytics.
4
4
  *
5
- * It will:
6
- * - Push a `searchQueryChanged` event whenever the search term changes.
7
- * - Push a `searchNoResults` event whenever a non-empty query returns zero results.
8
- * - Provide a click handler (`handleClick`) to log `searchResultClick` events.
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
- * @param {SearchState} state - The current search state, containing:
11
- * - `searchTerm` (string): the text the user has searched for.
12
- * - `results` (Array<any>): the array of result objects returned.
13
- * @returns {{ handleClick: (e: Event) => void }}
14
- * An object with a `handleClick` function that you can attach to result links to log clicks.
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
- var a = Object.defineProperty;
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. 'searchResultClick'
5
+ * @param eventName e.g. 'www:search_query_changed'
16
6
  * @param payload additional key/value data
17
7
  */
18
- push(e, c = {}) {
19
- this.dataLayer.push({ event: e, ...c });
8
+ track(n, c = {}) {
9
+ gtag("event", n, c);
20
10
  }
21
11
  }
22
- function m(r) {
23
- const e = L(new g());
24
- return l(() => {
25
- r != null && r.searchTerm && e.current.push("searchQueryChanged", { query: r == null ? void 0 : r.searchTerm });
26
- }, [r == null ? void 0 : r.searchTerm]), l(() => {
27
- r != null && r.results && r != null && r.searchTerm && r.results.length === 0 && e.current.push("searchNoResults", { query: r == null ? void 0 : r.searchTerm });
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: s((i) => {
30
- var u;
31
- const n = i.target.closest("a");
32
- if (!n) return;
33
- const o = (u = n.querySelector(".n-search-link-label")) == null ? void 0 : u.innerHTML, y = n.getAttribute("href");
34
- e.current.push("searchResultClick", {
35
- searchResultTitle: o,
36
- searchResultURL: y
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
- m as useSearchLogger
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.45",
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
+ }
@@ -0,0 +1,5 @@
1
+ import GoogleTagManager from "./GoogleTagManager.astro";
2
+ import GoogleAnalytics from "./GoogleAnalytics.astro";
3
+ import Analytics from "./analytics"
4
+
5
+ export { Analytics, GoogleAnalytics, GoogleTagManager }
@@ -1,30 +1,39 @@
1
1
  import type { SearchState } from '../types/search.types';
2
2
  import { useEffect, useRef, useCallback } from 'preact/hooks';
3
- import { Logger } from '../../../../analytics/logger';
3
+ import Analytics from '../../../../analytics/analytics';
4
4
 
5
5
  /**
6
- * Hook that logs search-related events to your analytics Logger.
6
+ * Custom hook that logs search-related events for analytics.
7
7
  *
8
- * It will:
9
- * - Push a `searchQueryChanged` event whenever the search term changes.
10
- * - Push a `searchNoResults` event whenever a non-empty query returns zero results.
11
- * - Provide a click handler (`handleClick`) to log `searchResultClick` events.
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
- * @param {SearchState} state - The current search state, containing:
14
- * - `searchTerm` (string): the text the user has searched for.
15
- * - `results` (Array<any>): the array of result objects returned.
16
- * @returns {{ handleClick: (e: Event) => void }}
17
- * An object with a `handleClick` function that you can attach to result links to log clicks.
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 loggerRef = useRef<Logger>(new Logger());
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
- loggerRef.current.push('searchQueryChanged', { query: state?.searchTerm });
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
- loggerRef.current.push('searchNoResults', { query: state?.searchTerm });
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
- loggerRef.current.push('searchResultClick', {
48
- searchResultTitle: label,
49
- searchResultURL: url,
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
- }