@probat/react 0.2.1 → 0.3.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/README.md CHANGED
@@ -1,379 +1,68 @@
1
1
  # @probat/react
2
2
 
3
- React library for Probat A/B testing and experimentation. This package provides a clean, reusable way to integrate Probat into your React applications without copying entire files into your repository.
3
+ React SDK for Probat A/B testing. Automatic impression and click tracking with zero boilerplate.
4
4
 
5
- ## Installation
5
+ ## Install
6
6
 
7
7
  ```bash
8
8
  npm install @probat/react
9
- # or
10
- yarn add @probat/react
11
- # or
12
- pnpm add @probat/react
13
9
  ```
14
10
 
15
11
  ## Quick Start
16
12
 
17
- ### 1. Wrap your app with ProbatProvider
13
+ Wrap your app root with the provider (Next.js App Router example):
18
14
 
19
15
  ```tsx
20
- import { ProbatProvider } from '@probat/react';
16
+ // app/providers.tsx
17
+ "use client";
18
+ import { ProbatProviderClient } from "@probat/react";
21
19
 
22
- function App() {
20
+ export function Providers({ children }: { children: React.ReactNode }) {
23
21
  return (
24
- <ProbatProvider clientKey="your-client-key" environment="dev">
25
- <YourApp />
26
- </ProbatProvider>
22
+ <ProbatProviderClient userId="your-user-uuid">
23
+ {children}
24
+ </ProbatProviderClient>
27
25
  );
28
26
  }
29
27
  ```
30
28
 
31
- ### 2. Use experiments in your components
32
-
33
- #### Option A: Using the `useExperiment` hook
29
+ Create a thin experiment wrapper one per experiment:
34
30
 
35
31
  ```tsx
36
- import { useExperiment } from '@probat/react';
37
-
38
- function MyComponent() {
39
- const { variantLabel, isLoading, trackClick } = useExperiment('proposal-id');
40
-
41
- if (isLoading) return <div>Loading...</div>;
32
+ // components/CTAButton/CTAButton.experiment.tsx
33
+ "use client";
34
+ import { Experiment } from "@probat/react";
35
+ import { CTAButton } from "./CTAButton";
36
+ import { CTAButtonAI } from "./CTAButton.ai-v1";
42
37
 
38
+ export function CTAButtonExperiment() {
43
39
  return (
44
- <div onClick={trackClick}>
45
- {variantLabel === 'control' ? (
46
- <ControlVariant />
47
- ) : (
48
- <ExperimentVariant />
49
- )}
50
- </div>
40
+ <Experiment
41
+ id="cta-copy-test"
42
+ control={<CTAButton />}
43
+ variants={{ ai_v1: <CTAButtonAI /> }}
44
+ />
51
45
  );
52
46
  }
53
47
  ```
54
48
 
55
- #### Option B: Using the `withExperiment` HOC (backward compatible)
56
-
57
- ```tsx
58
- import { withExperiment } from '@probat/react';
59
-
60
- const ControlComponent = () => <div>Control</div>;
61
- const VariantA = () => <div>Variant A</div>;
62
- const VariantB = () => <div>Variant B</div>;
63
-
64
- const ExperimentedComponent = withExperiment(ControlComponent, {
65
- proposalId: 'proposal-id',
66
- registry: {
67
- 'variant-a': VariantA,
68
- 'variant-b': VariantB,
69
- },
70
- });
71
- ```
49
+ That's it. Impressions (50% visible for 250ms) and clicks are tracked automatically.
72
50
 
73
- ### 3. Track metrics
51
+ ## Custom Metrics
74
52
 
75
53
  ```tsx
76
- import { useProbatMetrics } from '@probat/react';
77
-
78
- function Button() {
79
- const { trackClick, trackMetric } = useProbatMetrics();
80
-
81
- return (
82
- <button
83
- onClick={(e) => {
84
- trackClick(e, {
85
- proposalId: 'proposal-id',
86
- variantLabel: 'control',
87
- });
88
- }}
89
- >
90
- Click me
91
- </button>
92
- );
93
- }
54
+ const { capture } = useProbatMetrics();
55
+ capture("purchase", { revenue: 42 });
94
56
  ```
95
57
 
96
- ### 4. Heatmap Tracking (Automatic)
97
-
98
- Heatmap tracking is automatically enabled when you use `ProbatProvider`. It tracks:
99
- - **Click events**: User clicks across your website
100
- - **Cursor movements**: Mouse movement patterns for engagement analysis
101
-
102
- The tracking automatically associates data with the active experiment (`proposalId`) and variant (`variantLabel`) based on the experiment context. No additional configuration needed!
103
-
104
- ```tsx
105
- // Heatmap tracking is automatically initialized by ProbatProvider
106
- <ProbatProvider clientKey="your-key" environment="dev">
107
- <YourApp />
108
- </ProbatProvider>
109
- ```
110
-
111
- To manually control heatmap tracking:
112
-
113
- ```tsx
114
- import { initHeatmapTracking, stopHeatmapTracking } from '@probat/react';
115
-
116
- // Initialize with custom config
117
- initHeatmapTracking({
118
- apiBaseUrl: 'https://api.probat.com',
119
- batchSize: 10,
120
- batchInterval: 5000,
121
- trackCursor: true, // Enable cursor movement tracking
122
- });
123
-
124
- // Stop tracking when needed
125
- stopHeatmapTracking();
126
- ```
127
-
128
- ## API Reference
129
-
130
- ### `<ProbatProvider>`
131
-
132
- Wraps your app and provides Probat configuration to all child components.
133
-
134
- #### Props
135
-
136
- - `apiBaseUrl?: string` - The base URL for the Probat API. If not provided, will try to read from:
137
- - `VITE_PROBAT_API` (Vite)
138
- - `NEXT_PUBLIC_PROBAT_API` (Next.js)
139
- - `window.__PROBAT_API`
140
- - Default: `"https://gushi.onrender.com"`
141
- - `clientKey?: string` - Client key for identification (optional)
142
- - `environment?: "dev" | "prod"` - Explicitly set environment. If not provided, will auto-detect based on hostname.
143
- - `proposalId?: string` - Optional proposal/experiment ID for heatmap tracking segregation
144
- - `variantLabel?: string` - Optional variant label for heatmap tracking segregation
145
- - `children: React.ReactNode` - Your app components
146
-
147
- **Note**: `proposalId` and `variantLabel` are automatically set from localStorage when experiments are active. You typically don't need to pass these manually.
148
-
149
- #### Example
150
-
151
- ```tsx
152
- <ProbatProvider
153
- apiBaseUrl="https://api.probat.com"
154
- clientKey="your-key"
155
- environment="prod"
156
- >
157
- <App />
158
- </ProbatProvider>
159
- ```
160
-
161
- ### `useExperiment(proposalId, options?)`
162
-
163
- Hook for fetching and applying experiment variants.
164
-
165
- #### Parameters
166
-
167
- - `proposalId: string` - The proposal ID for the experiment
168
- - `options?: { autoTrackImpression?: boolean }` - Optional configuration
169
- - `autoTrackImpression` - Automatically track impression when variant is loaded (default: `true`)
170
-
171
- #### Returns
172
-
173
- - `variantLabel: string` - The current variant label (e.g., "control", "variant-a")
174
- - `experimentId: string | null` - The experiment ID
175
- - `isLoading: boolean` - Whether the experiment decision is still loading
176
- - `error: Error | null` - Any error that occurred while fetching the experiment
177
- - `trackClick: (event?: React.MouseEvent | null) => void` - Manually track a click for this experiment
178
-
179
- #### Example
180
-
181
- ```tsx
182
- const { variantLabel, isLoading, trackClick } = useExperiment('proposal-id', {
183
- autoTrackImpression: true,
184
- });
185
- ```
186
-
187
- ### `useProbatMetrics()`
188
-
189
- Hook for tracking Probat metrics (clicks, impressions, custom metrics).
190
-
191
- #### Returns
192
-
193
- - `trackClick(event?, options?)` - Track a click event
194
- - `event?: React.MouseEvent` - Optional React mouse event (will extract metadata automatically)
195
- - `options?: { force?: boolean; proposalId?: string; variantLabel?: string; dimensions?: Record<string, any> }`
196
- - `trackMetric(metricName, proposalId, variantLabel?, dimensions?)` - Track a custom metric
197
- - `trackImpression(proposalId, variantLabel?, experimentId?)` - Track an impression/view
198
-
199
- #### Example
200
-
201
- ```tsx
202
- const { trackClick, trackMetric, trackImpression } = useProbatMetrics();
203
-
204
- // Track click
205
- <button onClick={(e) => trackClick(e, { proposalId: 'id', variantLabel: 'control' })}>
206
- Click
207
- </button>
208
-
209
- // Track custom metric
210
- trackMetric('purchase', 'proposal-id', 'variant-a', { amount: 100 });
211
-
212
- // Track impression
213
- trackImpression('proposal-id', 'control', 'experiment-id');
214
- ```
215
-
216
- ### Heatmap Tracking Functions
217
-
218
- #### `initHeatmapTracking(config)`
58
+ ## Run the Example
219
59
 
220
- Initialize heatmap tracking with custom configuration.
221
-
222
- ##### Parameters
223
-
224
- - `config: HeatmapConfig` - Configuration object:
225
- - `apiBaseUrl: string` - Base URL for the heatmap API (default: from ProbatProvider)
226
- - `batchSize?: number` - Number of clicks to batch before sending (default: `10`)
227
- - `batchInterval?: number` - Time in ms to wait before sending batch (default: `5000`)
228
- - `trackCursor?: boolean` - Enable cursor movement tracking (default: `true`)
229
- - `cursorThrottle?: number` - Throttle cursor events in ms (default: `100`)
230
- - `cursorBatchSize?: number` - Number of cursor movements to batch (default: `50`)
231
- - `enabled?: boolean` - Enable/disable tracking (default: `true`)
232
- - `excludeSelectors?: string[]` - CSS selectors to exclude from tracking
233
- - `excludedOrigins?: string[]` - Origins to exclude from tracking
234
-
235
- ##### Example
236
-
237
- ```tsx
238
- import { initHeatmapTracking } from '@probat/react';
239
-
240
- initHeatmapTracking({
241
- apiBaseUrl: 'https://api.probat.com',
242
- batchSize: 20,
243
- trackCursor: true,
244
- cursorThrottle: 100,
245
- });
246
- ```
247
-
248
- #### `stopHeatmapTracking()`
249
-
250
- Stop heatmap tracking and send any pending batches.
251
-
252
- ##### Example
253
-
254
- ```tsx
255
- import { stopHeatmapTracking } from '@probat/react';
256
-
257
- stopHeatmapTracking();
258
- ```
259
-
260
- #### `getHeatmapTracker()`
261
-
262
- Get the current heatmap tracker instance (for advanced usage).
263
-
264
- ##### Returns
265
-
266
- - `HeatmapTracker | null` - The tracker instance or null if not initialized
267
-
268
- ### `withExperiment(Control, options)`
269
-
270
- Higher-Order Component for wrapping components with experiment variants (backward compatible with old injection pattern).
271
-
272
- #### Parameters
273
-
274
- - `Control: React.ComponentType` - The control/default component
275
- - `options: { proposalId: string; registry: Record<string, React.ComponentType> }`
276
- - `proposalId` - The proposal ID
277
- - `registry` - Map of variant labels to component types
278
-
279
- #### Returns
280
-
281
- A wrapped component that automatically selects and renders the correct variant.
282
-
283
- #### Example
284
-
285
- ```tsx
286
- const ExperimentedComponent = withExperiment(ControlComponent, {
287
- proposalId: 'proposal-id',
288
- registry: {
289
- 'variant-a': VariantA,
290
- 'variant-b': VariantB,
291
- },
292
- });
293
- ```
294
-
295
- ## Environment Detection
296
-
297
- The library automatically detects whether you're running in development or production based on the hostname:
298
-
299
- - **Development**: `localhost`, `127.0.0.1`, or local IP addresses (192.168.x.x, 10.x.x.x, 172.16-31.x.x)
300
- - **Production**: Everything else
301
-
302
- You can override this by passing the `environment` prop to `ProbatProvider`.
303
-
304
- ## Configuration
305
-
306
- ### Environment Variables
307
-
308
- You can configure the API base URL using environment variables:
309
-
310
- - **Vite**: `VITE_PROBAT_API=https://api.probat.com`
311
- - **Next.js**: `NEXT_PUBLIC_PROBAT_API=https://api.probat.com`
312
- - **Browser**: `window.__PROBAT_API = 'https://api.probat.com'`
313
-
314
- ### TypeScript
315
-
316
- The package includes TypeScript definitions. No additional `@types` package needed.
317
-
318
- ## Testing Locally
319
-
320
- 1. Build the package:
321
- ```bash
322
- cd packages/probat-react
323
- npm run build
324
- ```
325
-
326
- 2. Link it locally (optional, for testing in other projects):
327
- ```bash
328
- npm link
329
- ```
330
-
331
- 3. In your test project:
332
- ```bash
333
- npm link @probat/react
334
- ```
335
-
336
- ## Migration from Injected Files
337
-
338
- If you're currently using the injected file pattern, you can migrate gradually:
339
-
340
- 1. Install the package: `npm install @probat/react`
341
- 2. Wrap your app with `<ProbatProvider>`
342
- 3. Replace `withExperiment` imports:
343
- ```tsx
344
- // Old
345
- import { withExperiment } from '../../probat/runtime';
346
-
347
- // New
348
- import { withExperiment } from '@probat/react';
349
- ```
350
- 4. Remove the `probat/` directory from your repo
351
- 5. Update your component exports to use the new import
352
-
353
- ## Next.js Support
354
-
355
- The package is fully compatible with Next.js 13+ App Router and Pages Router. See [NEXTJS_GUIDE.md](./NEXTJS_GUIDE.md) for detailed Next.js integration instructions.
356
-
357
- ### Quick Next.js Example
358
-
359
- ```tsx
360
- // app/layout.tsx (App Router)
361
- import { ProbatProvider } from '@probat/react';
362
-
363
- export default function RootLayout({ children }) {
364
- return (
365
- <html>
366
- <body>
367
- <ProbatProvider clientKey="..." environment="dev">
368
- {children}
369
- </ProbatProvider>
370
- </body>
371
- </html>
372
- );
373
- }
60
+ ```bash
61
+ cd packages/probat-react/example
62
+ node mocks/server.ts & # mock API on :3001
63
+ npx next dev # app on :3000
374
64
  ```
375
65
 
376
- ## License
377
-
378
- MIT
66
+ ## Docs
379
67
 
68
+ See [SPEC.md](./SPEC.md) for the full event schemas, assignment rules, dedupe logic, and edge cases.