@sanity/personalization-plugin 2.5.0-launch-darkly.1 → 2.5.0
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/LICENSE +1 -1
- package/README.md +109 -4
- package/dist/index.js +11 -6886
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +11 -6886
- package/dist/index.mjs.map +1 -1
- package/package.json +23 -23
- package/src/components/ExperimentItem.tsx +10 -0
- package/src/components/VariantInput.tsx +2 -1
- package/src/fieldExperiments.tsx +3 -1
- package/src/launchDarkly/launchdarkly.md +0 -76
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -18,13 +18,15 @@ Once configured you can query the values using the ids of the experiment and var
|
|
|
18
18
|
- [Validation of individual array items](#validation-of-individual-array-items)
|
|
19
19
|
- [Shape of stored data](#shape-of-stored-data)
|
|
20
20
|
- [Querying data](#querying-data)
|
|
21
|
+
- [Split testing](#split-testing)
|
|
21
22
|
- [Using experiment fields in an array](#using-experiment-fields-in-an-array)
|
|
22
23
|
- [License](#license)
|
|
23
24
|
- [Develop \& test](#develop--test)
|
|
24
25
|
- [Release new version](#release-new-version)
|
|
25
26
|
- [License](#license-1)
|
|
26
27
|
|
|
27
|
-
For Specific information about the
|
|
28
|
+
For Specific information about the Growthbook FieldLevel export see its [readme](/growthbook.md)
|
|
29
|
+
For Specific information about the LaunchDarkly FieldLevel export see its [readme](/launchdarkly.md)
|
|
28
30
|
|
|
29
31
|
## Installation
|
|
30
32
|
|
|
@@ -206,7 +208,6 @@ This would also create two new fields in your schema.
|
|
|
206
208
|
|
|
207
209
|
Note that the name key in the field gets rewritten to value and is instead used to name the object field.
|
|
208
210
|
|
|
209
|
-
|
|
210
211
|
## Validation of individual array items
|
|
211
212
|
|
|
212
213
|
You may wish to validate individual fields for various reasons. From the variant array field, add a validation rule that can look through all the array items, and return item-specific validation messages at the path of that array item.
|
|
@@ -258,7 +259,8 @@ The custom input contains buttons which will add new array items with the experi
|
|
|
258
259
|
}
|
|
259
260
|
```
|
|
260
261
|
|
|
261
|
-
Querying data
|
|
262
|
+
## Querying data
|
|
263
|
+
|
|
262
264
|
Using GROQ filters you can query for a specific experiment, with a fallback to default value like so:
|
|
263
265
|
|
|
264
266
|
```ts
|
|
@@ -267,9 +269,112 @@ Using GROQ filters you can query for a specific experiment, with a fallback to d
|
|
|
267
269
|
}
|
|
268
270
|
```
|
|
269
271
|
|
|
272
|
+
## Split testing
|
|
273
|
+
|
|
274
|
+
Split testing involves splitting traffic for one url over 2+ pages, this is used when you want to test more than just a single field in an experiment.
|
|
275
|
+
|
|
276
|
+
### Studio Setup
|
|
277
|
+
|
|
278
|
+
To do split testing using this plugin define a type that can store a url path
|
|
279
|
+
|
|
280
|
+
```ts
|
|
281
|
+
export const path = defineType({
|
|
282
|
+
name: 'path',
|
|
283
|
+
type: 'string',
|
|
284
|
+
validation: (Rule) =>
|
|
285
|
+
Rule.required().custom(async (value: string | undefined, context) => {
|
|
286
|
+
if (!value) return true
|
|
287
|
+
if (!value.startsWith('/')) return 'Must start with "/"'
|
|
288
|
+
return true
|
|
289
|
+
}),
|
|
290
|
+
})
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
add the type to the studio `schema.types` and the plugin config fields:
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
fieldLevelExperiments({
|
|
297
|
+
fields: ['path', ...otherFields],
|
|
298
|
+
experiments: getExperiments,
|
|
299
|
+
}),
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
and then create a document type that stores the routing information:
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
export const routing = defineType({
|
|
306
|
+
name: 'routing',
|
|
307
|
+
type: 'document',
|
|
308
|
+
title: 'Routing Experiments',
|
|
309
|
+
fields: [
|
|
310
|
+
{
|
|
311
|
+
name: 'pathExperiment',
|
|
312
|
+
type: 'experimentPath',
|
|
313
|
+
initialValue: {active: true},
|
|
314
|
+
},
|
|
315
|
+
],
|
|
316
|
+
preview: {
|
|
317
|
+
select: {
|
|
318
|
+
path: 'pathExperiment.default',
|
|
319
|
+
experiment: 'pathExperiment.experimentId',
|
|
320
|
+
},
|
|
321
|
+
prepare({path, experiment}) {
|
|
322
|
+
return {
|
|
323
|
+
title: `${path} - ${experiment}`,
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
})
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
### Frontend usage
|
|
331
|
+
|
|
332
|
+
In your frontend you will need some middleware that can intercept the request for the page and check if the route is included in your split page testing and if so decide which page the user should see.
|
|
333
|
+
|
|
334
|
+
In Next.js Middleware it could be something like
|
|
335
|
+
|
|
336
|
+
```ts
|
|
337
|
+
const ROUTING_QUERY = defineQuery(`*[
|
|
338
|
+
_type == "routing" &&
|
|
339
|
+
pathExperiment.default == $path
|
|
340
|
+
][0]{
|
|
341
|
+
"route": coalesce(pathExperiment.variants[experimentId == $experimentId && variantId == $variantId][0].value, pathExperiment.default)
|
|
342
|
+
}`)
|
|
343
|
+
|
|
344
|
+
export async function middleware(request: NextRequest) {
|
|
345
|
+
let response = NextResponse.next()
|
|
346
|
+
const path = request.nextUrl.pathname
|
|
347
|
+
// getExperimentValue is a function that will determine a variant based on some user properties
|
|
348
|
+
const {variant} = await getExperimentValue(path)
|
|
349
|
+
|
|
350
|
+
const queryParams = {
|
|
351
|
+
path,
|
|
352
|
+
experimentId: 'homepage',
|
|
353
|
+
variantId: variant?.id || '',
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Instead of doing a query for every page this could be queried at build time and stored in a file.
|
|
357
|
+
const data = await client.fetch(ROUTING_QUERY, queryParams)
|
|
358
|
+
if (data?.route) {
|
|
359
|
+
const url = request.nextUrl.clone()
|
|
360
|
+
url.pathname = data.route
|
|
361
|
+
const rewrite = NextResponse.rewrite(url)
|
|
362
|
+
return rewrite
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return response
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
export const config = {
|
|
369
|
+
//only run the middleware on pages
|
|
370
|
+
matcher: ['/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)'],
|
|
371
|
+
}
|
|
372
|
+
```
|
|
373
|
+
|
|
270
374
|
## Using experiment fields in an array
|
|
271
375
|
|
|
272
376
|
You may want to add experiment fields as a type with in an array in oder to do this you would need to set an initial value for the experiment to active the schema would be something like:
|
|
377
|
+
|
|
273
378
|
```ts
|
|
274
379
|
defineField({
|
|
275
380
|
name: 'components',
|
|
@@ -284,7 +389,7 @@ defineField({
|
|
|
284
389
|
}),
|
|
285
390
|
```
|
|
286
391
|
|
|
287
|
-
You can then use a groq filter to return the base version of you array member so you don't have to create an experiment specific version
|
|
392
|
+
You can then use a groq filter to return the base version of you array member so you don't have to create an experiment specific version
|
|
288
393
|
|
|
289
394
|
```ts
|
|
290
395
|
*[
|