@elevateab/sdk 1.2.1 → 1.2.3
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 +230 -405
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @elevateab/sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A/B Testing SDK for Shopify Hydrogen and Next.js stores.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -9,523 +9,348 @@ npm install @elevateab/sdk
|
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
**Peer Dependencies:**
|
|
12
|
+
- `react` >= 18.0.0 or >= 19.0.0
|
|
13
|
+
- `@shopify/hydrogen` >= 2023.10.0 (Hydrogen only)
|
|
14
|
+
- `next` >= 13.0.0 (Next.js only)
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
- `@shopify/hydrogen` >= 2023.10.0 (optional - only for Hydrogen)
|
|
16
|
+
---
|
|
15
17
|
|
|
16
|
-
##
|
|
18
|
+
## Hydrogen Setup
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
| -------------------- | ------- | ------------------------------ |
|
|
20
|
-
| **Shopify Hydrogen** | ✅ Full | Automatic via `useAnalytics()` |
|
|
21
|
-
| **Next.js** | ✅ Full | Manual tracking functions |
|
|
22
|
-
| **Remix** | ✅ Full | Manual tracking functions |
|
|
23
|
-
| **Other React** | ✅ Full | Manual tracking functions |
|
|
20
|
+
Hydrogen uses automatic analytics tracking via Shopify's `useAnalytics()` hook.
|
|
24
21
|
|
|
25
|
-
|
|
22
|
+
### 1. Add the Provider
|
|
26
23
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```typescript
|
|
32
|
-
import { ElevateProvider } from "@elevateab/sdk";
|
|
24
|
+
```tsx
|
|
25
|
+
// app/root.tsx
|
|
26
|
+
import { ElevateProvider, ElevateAnalytics } from "@elevateab/sdk";
|
|
27
|
+
import { Analytics } from "@shopify/hydrogen";
|
|
33
28
|
|
|
34
29
|
export default function Root() {
|
|
35
30
|
const data = useLoaderData<typeof loader>();
|
|
36
31
|
|
|
37
32
|
return (
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
</
|
|
33
|
+
<Analytics.Provider cart={data.cart} shop={data.shop} consent={data.consent}>
|
|
34
|
+
<ElevateProvider
|
|
35
|
+
storeId="mystore.myshopify.com"
|
|
36
|
+
storefrontAccessToken={env.PUBLIC_STOREFRONT_API_TOKEN}
|
|
37
|
+
>
|
|
38
|
+
<ElevateAnalytics />
|
|
39
|
+
<Outlet />
|
|
40
|
+
</ElevateProvider>
|
|
41
|
+
</Analytics.Provider>
|
|
47
42
|
);
|
|
48
43
|
}
|
|
49
44
|
```
|
|
50
45
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
- ✅ Fetches test configurations from CDN
|
|
54
|
-
- ✅ Sets up Shopify Analytics tracking
|
|
55
|
-
- ✅ Tracks all analytics events with A/B test data
|
|
56
|
-
- ✅ Sends events to CloudFlare Worker
|
|
57
|
-
- ✅ Manages visitor IDs and session tracking
|
|
58
|
-
|
|
59
|
-
### 2. Use A/B Tests in Your Components
|
|
46
|
+
That's it. Analytics events are tracked automatically when users view pages, products, add to cart, etc.
|
|
60
47
|
|
|
61
|
-
|
|
62
|
-
import { useExperiment } from "@elevateab/sdk";
|
|
48
|
+
### 2. Add Anti-Flicker (Recommended)
|
|
63
49
|
|
|
64
|
-
|
|
65
|
-
const { variant, isLoading } = useExperiment("test-price-1");
|
|
50
|
+
Prevents content flash while tests load. Add this script to `<head>` before any other scripts:
|
|
66
51
|
|
|
67
|
-
|
|
52
|
+
```tsx
|
|
53
|
+
// app/root.tsx
|
|
54
|
+
import { getFlickerPreventionScript } from "@elevateab/sdk";
|
|
68
55
|
|
|
56
|
+
export default function Root() {
|
|
69
57
|
return (
|
|
70
|
-
<
|
|
71
|
-
|
|
72
|
-
|
|
58
|
+
<html>
|
|
59
|
+
<head>
|
|
60
|
+
<script dangerouslySetInnerHTML={{ __html: getFlickerPreventionScript() }} />
|
|
61
|
+
{/* other head elements */}
|
|
62
|
+
</head>
|
|
63
|
+
<body>{/* ... */}</body>
|
|
64
|
+
</html>
|
|
73
65
|
);
|
|
74
66
|
}
|
|
75
67
|
```
|
|
76
68
|
|
|
77
|
-
|
|
69
|
+
Then pass `preventFlickering` to the provider:
|
|
78
70
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
71
|
+
```tsx
|
|
72
|
+
<ElevateProvider
|
|
73
|
+
storeId="mystore.myshopify.com"
|
|
74
|
+
storefrontAccessToken={env.PUBLIC_STOREFRONT_API_TOKEN}
|
|
75
|
+
preventFlickering={true}
|
|
76
|
+
>
|
|
77
|
+
```
|
|
82
78
|
|
|
83
|
-
|
|
84
|
-
- `product_viewed` - Product views
|
|
85
|
-
- `product_added_to_cart` - Add to cart events
|
|
86
|
-
- `product_removed_from_cart` - Remove from cart events
|
|
87
|
-
- `cart_viewed` - Cart page views
|
|
79
|
+
---
|
|
88
80
|
|
|
89
|
-
|
|
81
|
+
## Next.js Setup
|
|
90
82
|
|
|
91
|
-
|
|
83
|
+
Next.js requires manual tracking since it doesn't have Shopify's analytics system.
|
|
92
84
|
|
|
93
|
-
|
|
94
|
-
- **Test Views** (`ab_test_views`) - Tests the user has actually viewed
|
|
95
|
-
- **UTM Parameters** - Campaign tracking data
|
|
96
|
-
- **Device/Browser Info** - User agent, device type, OS
|
|
97
|
-
- **Referrer Source** - Traffic source (Google, Facebook, Direct, etc.)
|
|
98
|
-
- **Product/Cart Data** - Product IDs, prices, quantities
|
|
85
|
+
### 1. Add the Provider
|
|
99
86
|
|
|
100
|
-
|
|
87
|
+
```tsx
|
|
88
|
+
// app/layout.tsx
|
|
89
|
+
import { ElevateNextProvider } from "@elevateab/sdk/next";
|
|
90
|
+
import { getFlickerPreventionScript } from "@elevateab/sdk";
|
|
101
91
|
|
|
102
|
-
|
|
92
|
+
export default function RootLayout({ children }) {
|
|
93
|
+
return (
|
|
94
|
+
<html>
|
|
95
|
+
<head>
|
|
96
|
+
{/* Anti-flicker script (recommended) */}
|
|
97
|
+
<script dangerouslySetInnerHTML={{ __html: getFlickerPreventionScript() }} />
|
|
98
|
+
</head>
|
|
99
|
+
<body>
|
|
100
|
+
<ElevateNextProvider
|
|
101
|
+
storeId="mystore.myshopify.com"
|
|
102
|
+
storefrontAccessToken={process.env.NEXT_PUBLIC_STOREFRONT_TOKEN}
|
|
103
|
+
preventFlickering={true}
|
|
104
|
+
>
|
|
105
|
+
{children}
|
|
106
|
+
</ElevateNextProvider>
|
|
107
|
+
</body>
|
|
108
|
+
</html>
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
```
|
|
103
112
|
|
|
104
|
-
|
|
113
|
+
The `ElevateNextProvider` automatically:
|
|
114
|
+
- Tracks page views on route changes
|
|
115
|
+
- Initializes analytics globally
|
|
116
|
+
- Handles anti-flicker reveal
|
|
105
117
|
|
|
106
|
-
###
|
|
118
|
+
### 2. Track Product Views
|
|
107
119
|
|
|
108
|
-
|
|
120
|
+
```tsx
|
|
121
|
+
// app/product/[handle]/page.tsx
|
|
122
|
+
import { ProductViewTracker } from "@elevateab/sdk/next";
|
|
109
123
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
124
|
+
export default function ProductPage({ product }) {
|
|
125
|
+
return (
|
|
126
|
+
<>
|
|
127
|
+
<ProductViewTracker
|
|
128
|
+
productId={product.id}
|
|
129
|
+
productVendor={product.vendor}
|
|
130
|
+
productPrice={parseFloat(product.priceRange.minVariantPrice.amount)}
|
|
131
|
+
currency={product.priceRange.minVariantPrice.currencyCode}
|
|
132
|
+
/>
|
|
133
|
+
{/* Product content */}
|
|
134
|
+
</>
|
|
135
|
+
);
|
|
136
|
+
}
|
|
120
137
|
```
|
|
121
138
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
139
|
+
### 3. Track Add to Cart
|
|
140
|
+
|
|
141
|
+
```tsx
|
|
142
|
+
import { trackAddToCart } from "@elevateab/sdk";
|
|
143
|
+
|
|
144
|
+
async function handleAddToCart() {
|
|
145
|
+
// Shopify GIDs are automatically converted to numeric IDs
|
|
146
|
+
await trackAddToCart({
|
|
147
|
+
productId: product.id, // "gid://shopify/Product/123" works
|
|
148
|
+
variantId: variant.id, // "gid://shopify/ProductVariant/456" works
|
|
149
|
+
productPrice: 99.99,
|
|
150
|
+
productQuantity: 1,
|
|
151
|
+
currency: "USD",
|
|
152
|
+
cartId: cart.id, // For cart attribute tagging
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
```
|
|
126
156
|
|
|
127
|
-
###
|
|
157
|
+
### 4. Other Tracking Events
|
|
128
158
|
|
|
129
|
-
|
|
159
|
+
```tsx
|
|
160
|
+
import {
|
|
161
|
+
trackRemoveFromCart,
|
|
162
|
+
trackCartView,
|
|
163
|
+
trackSearchSubmitted,
|
|
164
|
+
trackCheckoutStarted,
|
|
165
|
+
trackCheckoutCompleted,
|
|
166
|
+
} from "@elevateab/sdk";
|
|
130
167
|
|
|
131
|
-
|
|
132
|
-
await
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
variantId: "987654321",
|
|
168
|
+
// Remove from cart
|
|
169
|
+
await trackRemoveFromCart({
|
|
170
|
+
productId: product.id,
|
|
171
|
+
variantId: variant.id,
|
|
136
172
|
productPrice: 99.99,
|
|
137
173
|
productQuantity: 1,
|
|
138
|
-
currency: "USD",
|
|
139
|
-
// Add these to auto-update cart attributes:
|
|
140
|
-
cartId: cart.id,
|
|
141
|
-
storefrontAccessToken: process.env.NEXT_PUBLIC_STOREFRONT_TOKEN,
|
|
142
174
|
});
|
|
143
|
-
```
|
|
144
175
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
storefrontAccessToken: process.env.NEXT_PUBLIC_STOREFRONT_TOKEN,
|
|
176
|
+
// Cart view
|
|
177
|
+
await trackCartView({
|
|
178
|
+
cartTotalPrice: 199.99,
|
|
179
|
+
cartTotalQuantity: 2,
|
|
180
|
+
currency: "USD",
|
|
181
|
+
cartItems: [
|
|
182
|
+
{ productId: "123", variantId: "456", productPrice: 99.99, productQuantity: 1 },
|
|
183
|
+
],
|
|
154
184
|
});
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
> ✅ **The Storefront Access Token is SAFE to use client-side!**
|
|
158
|
-
> It's a PUBLIC token that Shopify designed for browser use. It can only read products and manage carts - it cannot do anything destructive.
|
|
159
185
|
|
|
160
|
-
|
|
186
|
+
// Search
|
|
187
|
+
await trackSearchSubmitted({ searchQuery: "blue shirt" });
|
|
161
188
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
} from "@elevateab/sdk";
|
|
169
|
-
|
|
170
|
-
const attributes = getCartAttributesPayload();
|
|
189
|
+
// Checkout started
|
|
190
|
+
await trackCheckoutStarted({
|
|
191
|
+
cartTotalPrice: 199.99,
|
|
192
|
+
currency: "USD",
|
|
193
|
+
cartItems: [...],
|
|
194
|
+
});
|
|
171
195
|
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
},
|
|
196
|
+
// Checkout completed (order placed)
|
|
197
|
+
await trackCheckoutCompleted({
|
|
198
|
+
orderId: "order_123",
|
|
199
|
+
cartTotalPrice: 199.99,
|
|
200
|
+
currency: "USD",
|
|
201
|
+
cartItems: [...],
|
|
179
202
|
});
|
|
180
203
|
```
|
|
181
204
|
|
|
182
|
-
|
|
205
|
+
---
|
|
183
206
|
|
|
184
|
-
|
|
207
|
+
## Using A/B Tests
|
|
185
208
|
|
|
186
|
-
|
|
187
|
-
import { useExperiment } from "@elevateab/sdk";
|
|
209
|
+
### useExperiment Hook
|
|
188
210
|
|
|
189
|
-
|
|
190
|
-
|
|
211
|
+
```tsx
|
|
212
|
+
import { useExperiment } from "@elevateab/sdk";
|
|
191
213
|
|
|
192
|
-
|
|
214
|
+
function PricingSection() {
|
|
215
|
+
const { variant, isLoading } = useExperiment("pricing-test");
|
|
193
216
|
|
|
194
|
-
|
|
195
|
-
if (variant?.isA) return <Price amount={99.99} label="Original" />;
|
|
196
|
-
if (variant?.isB) return <Price amount={89.99} label="Sale Price" />;
|
|
217
|
+
if (isLoading) return <LoadingSkeleton />;
|
|
197
218
|
|
|
198
|
-
|
|
219
|
+
if (variant?.isControl) {
|
|
220
|
+
return <Price amount={99.99} />;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return <Price amount={79.99} />;
|
|
199
224
|
}
|
|
200
225
|
```
|
|
201
226
|
|
|
202
|
-
###
|
|
227
|
+
### Variant Properties
|
|
203
228
|
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
const { variant } = useExperiment("test-headline-1");
|
|
229
|
+
```tsx
|
|
230
|
+
const { variant } = useExperiment("test-id");
|
|
207
231
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
</div>
|
|
216
|
-
);
|
|
217
|
-
}
|
|
232
|
+
variant?.isControl // true if control group
|
|
233
|
+
variant?.isA // true if variant A
|
|
234
|
+
variant?.isB // true if variant B
|
|
235
|
+
variant?.isC // true if variant C
|
|
236
|
+
variant?.isD // true if variant D
|
|
237
|
+
variant?.id // variant ID
|
|
238
|
+
variant?.name // variant name
|
|
218
239
|
```
|
|
219
240
|
|
|
220
|
-
### Multiple
|
|
241
|
+
### Multiple Variants
|
|
221
242
|
|
|
222
|
-
```
|
|
223
|
-
function
|
|
224
|
-
const { variant } = useExperiment("
|
|
243
|
+
```tsx
|
|
244
|
+
function LayoutTest() {
|
|
245
|
+
const { variant } = useExperiment("layout-test");
|
|
225
246
|
|
|
226
247
|
if (variant?.isA) return <LayoutA />;
|
|
227
248
|
if (variant?.isB) return <LayoutB />;
|
|
228
249
|
if (variant?.isC) return <LayoutC />;
|
|
229
|
-
if (variant?.isD) return <LayoutD />;
|
|
230
250
|
|
|
231
|
-
return <
|
|
251
|
+
return <DefaultLayout />;
|
|
232
252
|
}
|
|
233
253
|
```
|
|
234
254
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
### Cookie & Storage Management
|
|
238
|
-
|
|
239
|
-
The SDK uses cookies and session storage for state management:
|
|
240
|
-
|
|
241
|
-
**Cookies (1 year expiration):**
|
|
242
|
-
|
|
243
|
-
- `eabUserId` - Persistent visitor ID
|
|
244
|
-
- `ABTL` - Test assignments (which variant each test is assigned to)
|
|
245
|
-
- `ABAU` - Unique test views (which tests have been viewed)
|
|
246
|
-
- `_shopify_y` - Shopify client ID (read-only)
|
|
247
|
-
|
|
248
|
-
**Session Storage:**
|
|
255
|
+
---
|
|
249
256
|
|
|
250
|
-
|
|
251
|
-
- `ABAV` - Session test views
|
|
252
|
-
- `eabReferrer` - Entry referrer
|
|
253
|
-
- `eabEntry` - Entry page URL
|
|
257
|
+
## Preview Mode
|
|
254
258
|
|
|
255
|
-
|
|
259
|
+
Test specific variants without affecting live traffic. Add URL parameters:
|
|
256
260
|
|
|
257
|
-
```typescript
|
|
258
|
-
import {
|
|
259
|
-
assignVariant,
|
|
260
|
-
getTestList,
|
|
261
|
-
getVisitorId,
|
|
262
|
-
getSessionId,
|
|
263
|
-
parseAddViewData,
|
|
264
|
-
} from "@elevateab/sdk";
|
|
265
|
-
|
|
266
|
-
// Get current test assignments
|
|
267
|
-
const assignments = getTestList(); // { "test-1": "variant-a", "test-2": "control" }
|
|
268
|
-
|
|
269
|
-
// Get visitor ID
|
|
270
|
-
const visitorId = getVisitorId(); // "uuid-v4"
|
|
271
|
-
|
|
272
|
-
// Get session ID
|
|
273
|
-
const sessionId = getSessionId(); // "uuid-v4"
|
|
274
|
-
|
|
275
|
-
// Parse analytics data
|
|
276
|
-
const analyticsData = parseAddViewData({
|
|
277
|
-
referrer: document.referrer,
|
|
278
|
-
entryPage: window.location.href,
|
|
279
|
-
userAgent: navigator.userAgent,
|
|
280
|
-
});
|
|
281
|
-
// Returns: UTM params, device type, browser info, referrer source, etc.
|
|
282
261
|
```
|
|
283
|
-
|
|
284
|
-
### Device & Traffic Detection
|
|
285
|
-
|
|
286
|
-
```typescript
|
|
287
|
-
import {
|
|
288
|
-
getDeviceType,
|
|
289
|
-
getTrafficSource,
|
|
290
|
-
checkFacebookBrowser,
|
|
291
|
-
checkInstagramBrowser,
|
|
292
|
-
} from "@elevateab/sdk";
|
|
293
|
-
|
|
294
|
-
const device = getDeviceType(); // "desktop" | "tablet" | "mobile"
|
|
295
|
-
const source = getTrafficSource(); // "facebook" | "google" | "direct" | etc.
|
|
296
|
-
const isFB = checkFacebookBrowser(); // boolean
|
|
297
|
-
const isIG = checkInstagramBrowser(); // boolean
|
|
262
|
+
https://yourstore.com/?eabUserPreview=true&abtid=<test-id>&eab_tests=<short-id>_<variant-id>
|
|
298
263
|
```
|
|
299
264
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
For advanced use cases, access the analytics utilities directly:
|
|
303
|
-
|
|
304
|
-
```typescript
|
|
305
|
-
import {
|
|
306
|
-
extractProductId,
|
|
307
|
-
extractProductVariantId,
|
|
308
|
-
cleanCartToken,
|
|
309
|
-
sanitizeString,
|
|
310
|
-
roundToTwo,
|
|
311
|
-
} from "@elevateab/sdk";
|
|
312
|
-
|
|
313
|
-
// Extract IDs from Shopify GIDs
|
|
314
|
-
const productId = extractProductId("gid://shopify/Product/123456");
|
|
315
|
-
// Returns: "123456"
|
|
316
|
-
|
|
317
|
-
const variantId = extractProductVariantId(
|
|
318
|
-
"gid://shopify/ProductVariant/789012"
|
|
319
|
-
);
|
|
320
|
-
// Returns: "789012"
|
|
321
|
-
|
|
322
|
-
// Clean and sanitize data
|
|
323
|
-
const cleanToken = cleanCartToken("token?param=value");
|
|
324
|
-
const safeString = sanitizeString(userInput);
|
|
325
|
-
const price = roundToTwo(99.999); // 100.00
|
|
265
|
+
Example:
|
|
326
266
|
```
|
|
327
|
-
|
|
328
|
-
## Test Types Support
|
|
329
|
-
|
|
330
|
-
The SDK provides infrastructure for multiple test types:
|
|
331
|
-
|
|
332
|
-
- **Split URL Tests** - Redirect tests between different URLs
|
|
333
|
-
- **Price Tests** - A/B test different pricing strategies
|
|
334
|
-
- **Content Tests** - Test different copy, headlines, CTAs
|
|
335
|
-
- **Custom Code Tests** - Run custom JavaScript for advanced tests
|
|
336
|
-
|
|
337
|
-
### Example: Split URL Test
|
|
338
|
-
|
|
339
|
-
```typescript
|
|
340
|
-
function SplitURLTest() {
|
|
341
|
-
const { variant } = useExperiment("test-split-url-1");
|
|
342
|
-
|
|
343
|
-
useEffect(() => {
|
|
344
|
-
if (
|
|
345
|
-
variant?.handle &&
|
|
346
|
-
window.location.pathname !== `/products/${variant.handle}`
|
|
347
|
-
) {
|
|
348
|
-
window.location.href = `/products/${variant.handle}`;
|
|
349
|
-
}
|
|
350
|
-
}, [variant]);
|
|
351
|
-
|
|
352
|
-
return null;
|
|
353
|
-
}
|
|
267
|
+
https://yourstore.com/products/shirt?eabUserPreview=true&abtid=abc123&eab_tests=c123_12345
|
|
354
268
|
```
|
|
355
269
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
```typescript
|
|
359
|
-
function PriceTest({ originalPrice }: { originalPrice: number }) {
|
|
360
|
-
const { variant } = useExperiment("test-price-1");
|
|
270
|
+
Check if in preview mode:
|
|
361
271
|
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
: originalPrice;
|
|
272
|
+
```tsx
|
|
273
|
+
import { isPreviewMode } from "@elevateab/sdk";
|
|
365
274
|
|
|
366
|
-
|
|
275
|
+
if (isPreviewMode()) {
|
|
276
|
+
// Show preview indicator
|
|
367
277
|
}
|
|
368
278
|
```
|
|
369
279
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
### Components
|
|
280
|
+
---
|
|
373
281
|
|
|
374
|
-
|
|
282
|
+
## Cart Attribute Tagging
|
|
375
283
|
|
|
376
|
-
|
|
284
|
+
Orders are attributed to A/B tests via cart attributes. This happens automatically when you provide `storefrontAccessToken` and `cartId`.
|
|
377
285
|
|
|
378
|
-
|
|
379
|
-
interface ElevateProviderProps {
|
|
380
|
-
storeId: string; // Your Shopify store ID (required)
|
|
381
|
-
cart?: any; // Cart object from Shopify (Hydrogen only)
|
|
382
|
-
shop?: any; // Shop object from Shopify (Hydrogen only)
|
|
383
|
-
consent?: any; // Consent object from Shopify (Hydrogen only)
|
|
384
|
-
hasLocalizedPaths?: boolean; // Set true if homepage is at /en-us/, /fr-ca/, etc.
|
|
385
|
-
workerUrl?: string; // Custom CloudFlare Worker endpoint
|
|
386
|
-
ordersWorkerUrl?: string; // Custom orders endpoint for checkout_completed
|
|
387
|
-
children: React.ReactNode;
|
|
388
|
-
}
|
|
389
|
-
```
|
|
390
|
-
|
|
391
|
-
**Basic Usage:**
|
|
286
|
+
For Hydrogen, pass `storefrontAccessToken` to the provider:
|
|
392
287
|
|
|
393
|
-
```
|
|
288
|
+
```tsx
|
|
394
289
|
<ElevateProvider
|
|
395
290
|
storeId="mystore.myshopify.com"
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
consent={data.consent}
|
|
399
|
-
>
|
|
400
|
-
<Outlet />
|
|
401
|
-
</ElevateProvider>
|
|
291
|
+
storefrontAccessToken={env.PUBLIC_STOREFRONT_API_TOKEN}
|
|
292
|
+
/>
|
|
402
293
|
```
|
|
403
294
|
|
|
404
|
-
|
|
295
|
+
For Next.js, pass `cartId` to `trackAddToCart`:
|
|
405
296
|
|
|
406
|
-
```
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
>
|
|
414
|
-
<Outlet />
|
|
415
|
-
</ElevateProvider>
|
|
297
|
+
```tsx
|
|
298
|
+
trackAddToCart({
|
|
299
|
+
productId: "123",
|
|
300
|
+
variantId: "456",
|
|
301
|
+
cartId: cart.id,
|
|
302
|
+
// ...
|
|
303
|
+
});
|
|
416
304
|
```
|
|
417
305
|
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
#### `useExperiment(testId: string)`
|
|
306
|
+
The Storefront Access Token is safe to use client-side - it's a public token designed for browser use.
|
|
421
307
|
|
|
422
|
-
|
|
308
|
+
---
|
|
423
309
|
|
|
424
|
-
|
|
425
|
-
const { variant, isLoading } = useExperiment("test-id");
|
|
426
|
-
|
|
427
|
-
// variant properties:
|
|
428
|
-
// - id: string - Variant ID
|
|
429
|
-
// - name: string - Variant name
|
|
430
|
-
// - weight: number - Traffic percentage
|
|
431
|
-
// - isControl: boolean - Is this the control variant
|
|
432
|
-
// - isA, isB, isC, isD: boolean - Position flags
|
|
433
|
-
// - handle?: string - Product handle (for product tests)
|
|
434
|
-
// - price?: string - Price override (for price tests)
|
|
435
|
-
```
|
|
310
|
+
## Utility Functions
|
|
436
311
|
|
|
437
|
-
|
|
312
|
+
### Shopify ID Helpers
|
|
438
313
|
|
|
439
|
-
|
|
314
|
+
```tsx
|
|
315
|
+
import { extractShopifyId, isShopifyGid } from "@elevateab/sdk";
|
|
440
316
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
// config.tests: Test[] - All active tests
|
|
317
|
+
extractShopifyId("gid://shopify/Product/123456"); // "123456"
|
|
318
|
+
isShopifyGid("gid://shopify/Product/123"); // true
|
|
444
319
|
```
|
|
445
320
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
#### Cart Attributes
|
|
449
|
-
|
|
450
|
-
- `updateCartAttributes(cartId, options)` - Update cart with test data
|
|
451
|
-
- `cleanupCartAttributes(cartId, options)` - Remove test data from cart
|
|
452
|
-
- `getCartAttributesPayload()` - Get current test data as attributes array
|
|
453
|
-
|
|
454
|
-
#### Storage
|
|
455
|
-
|
|
456
|
-
- `getVisitorId()` - Get or create visitor ID
|
|
457
|
-
- `getSessionId()` - Get or create session ID
|
|
458
|
-
- `getTestList()` - Get current test assignments
|
|
459
|
-
- `setCookie(name, value)` - Set a cookie (1 year expiration)
|
|
460
|
-
- `getCookie(name)` - Get a cookie value
|
|
461
|
-
|
|
462
|
-
#### Analytics
|
|
463
|
-
|
|
464
|
-
- `parseAddViewData(params)` - Parse UTM params, device info, referrer
|
|
465
|
-
- `getPageTypeFromPathname(path)` - Detect page type
|
|
466
|
-
- `extractProductId(gid)` - Extract product ID from Shopify GID
|
|
467
|
-
- `extractProductVariantId(gid)` - Extract variant ID from Shopify GID
|
|
468
|
-
|
|
469
|
-
## Features
|
|
321
|
+
Note: Tracking functions automatically extract IDs, so you rarely need these directly.
|
|
470
322
|
|
|
471
|
-
|
|
472
|
-
- ✅ **React Server Components** - Compatible with RSC
|
|
473
|
-
- ✅ **Analytics Tracking** - Automatic event tracking via @shopify/hydrogen
|
|
474
|
-
- ✅ **Cart Tagging** - Attribute orders to A/B tests
|
|
475
|
-
- ✅ **Device Detection** - Mobile, tablet, desktop detection
|
|
476
|
-
- ✅ **Traffic Source Detection** - Facebook, Instagram, Google, etc.
|
|
477
|
-
- ✅ **UTM Parameter Tracking** - Campaign attribution
|
|
478
|
-
- ✅ **ESM & CJS** - Dual format for maximum compatibility
|
|
479
|
-
- ✅ **Tree-shakeable** - Import only what you need
|
|
480
|
-
- ✅ **Minified** - Optimized bundle size
|
|
323
|
+
---
|
|
481
324
|
|
|
482
|
-
##
|
|
483
|
-
|
|
484
|
-
- **Shopify Hydrogen**: ✅ Full support (2023.10.0+) - Automatic analytics
|
|
485
|
-
- **Next.js**: ✅ Full support (13+, 14+, 15+) - Manual tracking ([See guide](./NEXTJS_EXAMPLE.md))
|
|
486
|
-
- **Remix**: ✅ Full support - Manual tracking
|
|
487
|
-
- **React**: 18.0.0+
|
|
488
|
-
- **TypeScript**: 5.0.0+
|
|
489
|
-
- **Node.js**: 18.0.0+
|
|
490
|
-
|
|
491
|
-
## Quick Start Guides
|
|
492
|
-
|
|
493
|
-
- **Hydrogen**: See below for automatic analytics setup
|
|
494
|
-
- **Next.js**: See [Next.js Example](./NEXTJS_EXAMPLE.md) for manual tracking
|
|
495
|
-
|
|
496
|
-
## 🧪 Sandbox Stores for Testing
|
|
497
|
-
|
|
498
|
-
We've set up complete sandbox Shopify stores to help you test the Elevate SDK:
|
|
499
|
-
|
|
500
|
-
### Hydrogen Sandbox Store
|
|
501
|
-
|
|
502
|
-
**Location:** `hydrogen-shopify-sandbox/`
|
|
503
|
-
|
|
504
|
-
A fully functional Shopify Hydrogen storefront with Elevate SDK integration.
|
|
325
|
+
## API Reference
|
|
505
326
|
|
|
506
|
-
|
|
327
|
+
### ElevateProvider Props
|
|
507
328
|
|
|
508
|
-
```
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
329
|
+
```tsx
|
|
330
|
+
interface ElevateProviderProps {
|
|
331
|
+
storeId: string; // Required: your-store.myshopify.com
|
|
332
|
+
storefrontAccessToken?: string; // For cart attribute tagging
|
|
333
|
+
preventFlickering?: boolean; // Enable anti-flicker (default: false)
|
|
334
|
+
hasLocalizedPaths?: boolean; // True if homepage is at /en-us/, /fr-ca/, etc.
|
|
335
|
+
children: React.ReactNode;
|
|
336
|
+
}
|
|
512
337
|
```
|
|
513
338
|
|
|
514
|
-
|
|
515
|
-
- ✅ Complete ElevateProvider integration
|
|
516
|
-
- ✅ Automatic analytics tracking
|
|
517
|
-
- ✅ Cart attribute tagging
|
|
518
|
-
- ✅ Multiple A/B test examples
|
|
519
|
-
- ✅ Real Shopify products and cart
|
|
339
|
+
### Analytics Events Summary
|
|
520
340
|
|
|
521
|
-
|
|
341
|
+
| Event | Hydrogen | Next.js |
|
|
342
|
+
|-------|----------|---------|
|
|
343
|
+
| Page view | Automatic | Automatic |
|
|
344
|
+
| Product view | Automatic | `ProductViewTracker` |
|
|
345
|
+
| Add to cart | Automatic | `trackAddToCart()` |
|
|
346
|
+
| Remove from cart | Automatic | `trackRemoveFromCart()` |
|
|
347
|
+
| Cart view | Automatic | `trackCartView()` |
|
|
348
|
+
| Search | Automatic | `trackSearchSubmitted()` |
|
|
349
|
+
| Checkout started | Automatic | `trackCheckoutStarted()` |
|
|
350
|
+
| Checkout completed | Automatic | `trackCheckoutCompleted()` |
|
|
522
351
|
|
|
523
|
-
|
|
352
|
+
---
|
|
524
353
|
|
|
525
354
|
## License
|
|
526
355
|
|
|
527
356
|
MIT
|
|
528
|
-
|
|
529
|
-
## Support
|
|
530
|
-
|
|
531
|
-
For issues and questions, please visit [GitHub Issues](https://github.com/abshop/headless-npm/issues).
|