react-event-tracking 1.0.0 → 1.0.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/dist/index.cjs ADDED
@@ -0,0 +1,53 @@
1
+ 'use strict';
2
+
3
+ const React = require('react');
4
+
5
+ function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
6
+
7
+ const React__default = /*#__PURE__*/_interopDefaultCompat(React);
8
+
9
+ const AnalyticsContext = React__default.createContext(null);
10
+ const useAnalytics = () => {
11
+ const ctx = React.useContext(AnalyticsContext);
12
+ if (!ctx) {
13
+ throw new Error("useAnalytics must be used within AnalyticsRoot");
14
+ }
15
+ return ctx;
16
+ };
17
+ const AnalyticsRoot = ({
18
+ onEvent,
19
+ children
20
+ }) => {
21
+ const onEventRef = React.useRef(onEvent);
22
+ onEventRef.current = onEvent;
23
+ const sendEvent = React.useCallback((eventName, params) => {
24
+ onEventRef.current(eventName, params);
25
+ }, []);
26
+ const value = React.useMemo(() => ({ sendEvent }), [sendEvent]);
27
+ return /* @__PURE__ */ React__default.createElement(AnalyticsContext.Provider, { value }, children);
28
+ };
29
+ const AnalyticsProvider = ({
30
+ params,
31
+ children
32
+ }) => {
33
+ const ctx = useAnalytics();
34
+ const paramsRef = React.useRef(params);
35
+ paramsRef.current = params;
36
+ const sendEvent = React.useCallback(
37
+ (eventName, eventParams) => {
38
+ const currentParams = paramsRef.current;
39
+ ctx.sendEvent(eventName, {
40
+ ...currentParams,
41
+ ...eventParams
42
+ });
43
+ },
44
+ [ctx]
45
+ );
46
+ const value = React.useMemo(() => ({ sendEvent }), [sendEvent]);
47
+ return /* @__PURE__ */ React__default.createElement(AnalyticsContext.Provider, { value }, children);
48
+ };
49
+
50
+ exports.AnalyticsContext = AnalyticsContext;
51
+ exports.AnalyticsProvider = AnalyticsProvider;
52
+ exports.AnalyticsRoot = AnalyticsRoot;
53
+ exports.useAnalytics = useAnalytics;
@@ -0,0 +1,18 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React, { PropsWithChildren } from 'react';
3
+
4
+ type AnalyticsParams = Record<string, any>;
5
+ interface AnalyticsContextValue {
6
+ sendEvent: (eventName: string, params?: AnalyticsParams) => void;
7
+ }
8
+ declare const AnalyticsContext: React.Context<AnalyticsContextValue | null>;
9
+ declare const useAnalytics: () => AnalyticsContextValue;
10
+ declare const AnalyticsRoot: ({ onEvent, children }: PropsWithChildren<{
11
+ onEvent: (eventName: string, params?: AnalyticsParams) => void;
12
+ }>) => react_jsx_runtime.JSX.Element;
13
+ declare const AnalyticsProvider: ({ params, children }: PropsWithChildren<{
14
+ params: AnalyticsParams;
15
+ }>) => react_jsx_runtime.JSX.Element;
16
+
17
+ export { AnalyticsContext, AnalyticsProvider, AnalyticsRoot, useAnalytics };
18
+ export type { AnalyticsParams };
@@ -0,0 +1,18 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React, { PropsWithChildren } from 'react';
3
+
4
+ type AnalyticsParams = Record<string, any>;
5
+ interface AnalyticsContextValue {
6
+ sendEvent: (eventName: string, params?: AnalyticsParams) => void;
7
+ }
8
+ declare const AnalyticsContext: React.Context<AnalyticsContextValue | null>;
9
+ declare const useAnalytics: () => AnalyticsContextValue;
10
+ declare const AnalyticsRoot: ({ onEvent, children }: PropsWithChildren<{
11
+ onEvent: (eventName: string, params?: AnalyticsParams) => void;
12
+ }>) => react_jsx_runtime.JSX.Element;
13
+ declare const AnalyticsProvider: ({ params, children }: PropsWithChildren<{
14
+ params: AnalyticsParams;
15
+ }>) => react_jsx_runtime.JSX.Element;
16
+
17
+ export { AnalyticsContext, AnalyticsProvider, AnalyticsRoot, useAnalytics };
18
+ export type { AnalyticsParams };
@@ -0,0 +1,18 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import React, { PropsWithChildren } from 'react';
3
+
4
+ type AnalyticsParams = Record<string, any>;
5
+ interface AnalyticsContextValue {
6
+ sendEvent: (eventName: string, params?: AnalyticsParams) => void;
7
+ }
8
+ declare const AnalyticsContext: React.Context<AnalyticsContextValue | null>;
9
+ declare const useAnalytics: () => AnalyticsContextValue;
10
+ declare const AnalyticsRoot: ({ onEvent, children }: PropsWithChildren<{
11
+ onEvent: (eventName: string, params?: AnalyticsParams) => void;
12
+ }>) => react_jsx_runtime.JSX.Element;
13
+ declare const AnalyticsProvider: ({ params, children }: PropsWithChildren<{
14
+ params: AnalyticsParams;
15
+ }>) => react_jsx_runtime.JSX.Element;
16
+
17
+ export { AnalyticsContext, AnalyticsProvider, AnalyticsRoot, useAnalytics };
18
+ export type { AnalyticsParams };
package/dist/index.mjs ADDED
@@ -0,0 +1,44 @@
1
+ import React, { useContext, useRef, useCallback, useMemo } from 'react';
2
+
3
+ const AnalyticsContext = React.createContext(null);
4
+ const useAnalytics = () => {
5
+ const ctx = useContext(AnalyticsContext);
6
+ if (!ctx) {
7
+ throw new Error("useAnalytics must be used within AnalyticsRoot");
8
+ }
9
+ return ctx;
10
+ };
11
+ const AnalyticsRoot = ({
12
+ onEvent,
13
+ children
14
+ }) => {
15
+ const onEventRef = useRef(onEvent);
16
+ onEventRef.current = onEvent;
17
+ const sendEvent = useCallback((eventName, params) => {
18
+ onEventRef.current(eventName, params);
19
+ }, []);
20
+ const value = useMemo(() => ({ sendEvent }), [sendEvent]);
21
+ return /* @__PURE__ */ React.createElement(AnalyticsContext.Provider, { value }, children);
22
+ };
23
+ const AnalyticsProvider = ({
24
+ params,
25
+ children
26
+ }) => {
27
+ const ctx = useAnalytics();
28
+ const paramsRef = useRef(params);
29
+ paramsRef.current = params;
30
+ const sendEvent = useCallback(
31
+ (eventName, eventParams) => {
32
+ const currentParams = paramsRef.current;
33
+ ctx.sendEvent(eventName, {
34
+ ...currentParams,
35
+ ...eventParams
36
+ });
37
+ },
38
+ [ctx]
39
+ );
40
+ const value = useMemo(() => ({ sendEvent }), [sendEvent]);
41
+ return /* @__PURE__ */ React.createElement(AnalyticsContext.Provider, { value }, children);
42
+ };
43
+
44
+ export { AnalyticsContext, AnalyticsProvider, AnalyticsRoot, useAnalytics };
package/package.json CHANGED
@@ -1,6 +1,9 @@
1
1
  {
2
2
  "name": "react-event-tracking",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
+ "files": [
5
+ "dist"
6
+ ],
4
7
  "exports": {
5
8
  ".": {
6
9
  "types": "./dist/index.d.ts",
@@ -32,4 +35,4 @@
32
35
  "react": ">=18.0.0",
33
36
  "react-dom": ">=18.0.0"
34
37
  }
35
- }
38
+ }
package/src/index.tsx DELETED
@@ -1,70 +0,0 @@
1
- import React, {
2
- useCallback,
3
- useContext,
4
- useMemo,
5
- useRef,
6
- type PropsWithChildren
7
- } from "react"
8
-
9
- export type AnalyticsParams = Record<string, any>
10
-
11
- interface AnalyticsContextValue {
12
- sendEvent: (eventName: string, params?: AnalyticsParams) => void
13
- }
14
-
15
- export const AnalyticsContext = React.createContext<AnalyticsContextValue | null>(null)
16
-
17
- export const useAnalytics = () => {
18
- const ctx = useContext(AnalyticsContext)
19
- if (!ctx) {
20
- throw new Error("useAnalytics must be used within AnalyticsRoot")
21
- }
22
-
23
- return ctx
24
- }
25
-
26
- export const AnalyticsRoot = ({
27
- onEvent,
28
- children
29
- }: PropsWithChildren<{
30
- onEvent: (eventName: string, params?: AnalyticsParams) => void
31
- }>) => {
32
- const onEventRef = useRef(onEvent)
33
- onEventRef.current = onEvent
34
-
35
- const sendEvent = useCallback((eventName: string, params?: AnalyticsParams) => {
36
- onEventRef.current(eventName, params)
37
- }, [])
38
-
39
- const value = useMemo(() => ({ sendEvent }), [sendEvent])
40
-
41
- return <AnalyticsContext.Provider value={value}>{children}</AnalyticsContext.Provider>
42
- }
43
-
44
- export const AnalyticsProvider = ({
45
- params,
46
- children
47
- }: PropsWithChildren<{
48
- params: AnalyticsParams
49
- }>) => {
50
- const ctx = useAnalytics()
51
-
52
- const paramsRef = useRef(params)
53
- paramsRef.current = params
54
-
55
- const sendEvent = useCallback(
56
- (eventName: string, eventParams?: AnalyticsParams) => {
57
- const currentParams = paramsRef.current
58
-
59
- ctx.sendEvent(eventName, {
60
- ...currentParams,
61
- ...eventParams
62
- })
63
- },
64
- [ctx]
65
- )
66
-
67
- const value = useMemo(() => ({ sendEvent }), [sendEvent])
68
-
69
- return <AnalyticsContext.Provider value={value}>{children}</AnalyticsContext.Provider>
70
- }
@@ -1,162 +0,0 @@
1
- import { describe, it, expect, vi } from "vitest"
2
- import React from "react"
3
- import { render, screen } from "@testing-library/react"
4
- import userEvent from "@testing-library/user-event"
5
- import { AnalyticsRoot, AnalyticsProvider, useAnalytics } from "../src"
6
-
7
- const TestButton = ({
8
- eventName,
9
- params,
10
- label = "Click me"
11
- }: {
12
- eventName: string
13
- params?: Record<string, any>
14
- label?: string
15
- }) => {
16
- const { sendEvent } = useAnalytics()
17
- return <button onClick={() => sendEvent(eventName, params)}>{label}</button>
18
- }
19
-
20
- describe("Analytics Context", () => {
21
- it("should send event from root", async () => {
22
- const onEvent = vi.fn()
23
-
24
- render(
25
- <AnalyticsRoot onEvent={onEvent}>
26
- <TestButton
27
- eventName="test_click"
28
- params={{ foo: "bar" }}
29
- label="Root Click"
30
- />
31
- </AnalyticsRoot>
32
- )
33
-
34
- await userEvent.click(screen.getByText("Root Click"))
35
-
36
- expect(onEvent).toHaveBeenCalledTimes(1)
37
- expect(onEvent).toHaveBeenCalledWith("test_click", { foo: "bar" })
38
- })
39
-
40
- it("should merge params from nested providers", async () => {
41
- const onEvent = vi.fn()
42
-
43
- render(
44
- <AnalyticsRoot onEvent={onEvent}>
45
- <AnalyticsProvider params={{ section: "header" }}>
46
- <AnalyticsProvider params={{ item: "logo" }}>
47
- <TestButton
48
- eventName="logo_click"
49
- params={{ action: "click" }}
50
- label="Nested Click"
51
- />
52
- </AnalyticsProvider>
53
- </AnalyticsProvider>
54
- </AnalyticsRoot>
55
- )
56
-
57
- await userEvent.click(screen.getByText("Nested Click"))
58
-
59
- expect(onEvent).toHaveBeenCalledWith("logo_click", {
60
- section: "header",
61
- item: "logo",
62
- action: "click"
63
- })
64
- })
65
-
66
- it("should override params from child to parent", async () => {
67
- const onEvent = vi.fn()
68
-
69
- render(
70
- <AnalyticsRoot onEvent={onEvent}>
71
- <AnalyticsProvider params={{ page: "home", id: 1 }}>
72
- {/* Переопределяем id */}
73
- <AnalyticsProvider params={{ id: 2 }}>
74
- <TestButton
75
- eventName="click"
76
- params={{ id: 3 }}
77
- label="Override Click"
78
- />
79
- </AnalyticsProvider>
80
- </AnalyticsProvider>
81
- </AnalyticsRoot>
82
- )
83
-
84
- await userEvent.click(screen.getByText("Override Click"))
85
-
86
- expect(onEvent).toHaveBeenCalledWith("click", {
87
- page: "home",
88
- id: 3
89
- })
90
- })
91
-
92
- it("should use latest params without re-rendering children", async () => {
93
- const onEvent = vi.fn()
94
-
95
- const Wrapper = ({ count }: { count: number }) => (
96
- <AnalyticsProvider params={{ count }}>
97
- <TestButton eventName="count_click" label="Rerender Click" />
98
- </AnalyticsProvider>
99
- )
100
-
101
- const { rerender } = render(
102
- <AnalyticsRoot onEvent={onEvent}>
103
- <Wrapper count={1} />
104
- </AnalyticsRoot>
105
- )
106
-
107
- await userEvent.click(screen.getByText("Rerender Click"))
108
- expect(onEvent).toHaveBeenLastCalledWith("count_click", { count: 1 })
109
-
110
- rerender(
111
- <AnalyticsRoot onEvent={onEvent}>
112
- <Wrapper count={2} />
113
- </AnalyticsRoot>
114
- )
115
-
116
- await userEvent.click(screen.getByText("Rerender Click"))
117
- expect(onEvent).toHaveBeenLastCalledWith("count_click", { count: 2 })
118
- })
119
-
120
- it("should NOT trigger re-render in consumers when params update (optimization check)", () => {
121
- const renderFn = vi.fn()
122
-
123
- const MemoChild = React.memo(() => {
124
- useAnalytics()
125
- renderFn()
126
- return <div>Memo Child</div>
127
- })
128
-
129
- // eslint-disable-next-line react/display-name
130
- MemoChild.displayName = "MemoChild"
131
-
132
- const { rerender } = render(
133
- <AnalyticsRoot onEvent={() => {}}>
134
- <AnalyticsProvider params={{ val: 1 }}>
135
- <MemoChild />
136
- </AnalyticsProvider>
137
- </AnalyticsRoot>
138
- )
139
-
140
- expect(renderFn).toHaveBeenCalledTimes(1)
141
-
142
- rerender(
143
- <AnalyticsRoot onEvent={() => {}}>
144
- <AnalyticsProvider params={{ val: 2 }}>
145
- <MemoChild />
146
- </AnalyticsProvider>
147
- </AnalyticsRoot>
148
- )
149
-
150
- expect(renderFn).toHaveBeenCalledTimes(1)
151
- })
152
-
153
- it("should throw error if used outside of AnalyticsRoot", () => {
154
- const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {})
155
-
156
- expect(() => {
157
- render(<TestButton eventName="fail" label="Fail Click" />)
158
- }).toThrow("useAnalytics must be used within AnalyticsRoot")
159
-
160
- consoleSpy.mockRestore()
161
- })
162
- })
package/tests/setup.ts DELETED
@@ -1,6 +0,0 @@
1
- import { afterEach } from 'vitest'
2
- import { cleanup } from '@testing-library/react'
3
-
4
- afterEach(() => {
5
- cleanup()
6
- })
package/tsconfig.json DELETED
@@ -1,17 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "jsx": "react-jsx",
6
- "declaration": true,
7
- "declarationMap": true,
8
- "outDir": "./dist",
9
- "rootDir": "./src",
10
- "strict": true,
11
- "moduleResolution": "node",
12
- "esModuleInterop": true,
13
- "skipLibCheck": true
14
- },
15
- "include": ["src/**/*"],
16
- "exclude": ["node_modules", "dist"]
17
- }
package/vitest.config.ts DELETED
@@ -1,10 +0,0 @@
1
- import { defineConfig } from 'vitest/config'
2
-
3
-
4
- export default defineConfig({
5
- test: {
6
- globals: true,
7
- environment: 'jsdom',
8
- setupFiles: './tests/setup.ts',
9
- },
10
- })