@sanity/personalization-plugin 2.1.0-growthbook.1 → 2.1.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/README.md +60 -6
- package/dist/index.d.mts +20 -215
- package/dist/index.d.ts +20 -215
- package/dist/index.js +165 -158
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +166 -160
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -2
- package/src/components/Array.tsx +11 -12
- package/src/components/ExperimentContext.tsx +10 -8
- package/src/components/ExperimentField.tsx +71 -39
- package/src/components/ExperimentInput.tsx +5 -2
- package/src/components/VariantInput.tsx +15 -68
- package/src/fieldExperiments.tsx +99 -34
- package/src/index.ts +0 -1
- package/src/types.ts +19 -191
- package/src/components/Secrets.tsx +0 -47
- package/src/growthbookFieldExperiments.tsx +0 -51
- package/src/utils/growthbook.ts +0 -78
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sanity/personalization-plugin",
|
|
3
|
-
"version": "2.1.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Plugin to help with personalization, a/b testing when using Sanity",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"sanity",
|
|
@@ -45,7 +45,6 @@
|
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@sanity/incompatible-plugin": "^1.0.4",
|
|
48
|
-
"@sanity/studio-secrets": "^3.0.0",
|
|
49
48
|
"@sanity/ui": "^2.8.19",
|
|
50
49
|
"@sanity/uuid": "^3.0.2",
|
|
51
50
|
"fast-deep-equal": "^3.1.3",
|
package/src/components/Array.tsx
CHANGED
|
@@ -8,44 +8,43 @@ import {useExperimentContext} from './ExperimentContext'
|
|
|
8
8
|
|
|
9
9
|
export const ArrayInput = (props: ArrayInputProps) => {
|
|
10
10
|
const fieldPath = props.path.slice(0, -1)
|
|
11
|
-
const
|
|
11
|
+
const {onItemAppend, variantName, variantId, experimentId} = props
|
|
12
|
+
const experimentValue = useFormValue([...fieldPath, experimentId])
|
|
12
13
|
|
|
13
14
|
const {experiments} = useExperimentContext()
|
|
14
15
|
|
|
15
|
-
const {onItemAppend, objectName} = props
|
|
16
|
-
|
|
17
16
|
const handleClick = useCallback(
|
|
18
17
|
async (variant: VariantType) => {
|
|
19
18
|
const item = {
|
|
20
19
|
_key: uuid(),
|
|
21
|
-
variantId: variant.id,
|
|
22
|
-
experimentId:
|
|
23
|
-
_type:
|
|
20
|
+
[variantId]: variant.id,
|
|
21
|
+
[experimentId]: experimentValue,
|
|
22
|
+
_type: variantName,
|
|
24
23
|
}
|
|
25
24
|
|
|
26
25
|
// Patch the document
|
|
27
26
|
onItemAppend(item)
|
|
28
27
|
},
|
|
29
|
-
[experimentId,
|
|
28
|
+
[variantId, experimentId, experimentValue, variantName, onItemAppend],
|
|
30
29
|
)
|
|
31
30
|
|
|
32
31
|
const filteredVariants =
|
|
33
32
|
experiments.find((option) => {
|
|
34
|
-
return option.id ===
|
|
33
|
+
return option.id === experimentValue
|
|
35
34
|
})?.variants || []
|
|
36
35
|
|
|
37
36
|
type Value = {
|
|
38
|
-
experimentId: string
|
|
39
37
|
value?: unknown
|
|
38
|
+
[key: string]: string | unknown
|
|
40
39
|
variantId: string
|
|
41
40
|
_key: string
|
|
42
41
|
_type: string
|
|
43
42
|
}
|
|
44
43
|
|
|
45
44
|
// there is probably some better was of getting the type of this?
|
|
46
|
-
const values = props.value as Value[]
|
|
45
|
+
const values = (props.value as Value[]) || []
|
|
47
46
|
|
|
48
|
-
const usedVariants = values?.map((variant) => variant
|
|
47
|
+
const usedVariants = values?.map((variant) => variant[variantId])
|
|
49
48
|
|
|
50
49
|
return (
|
|
51
50
|
<Stack space={3}>
|
|
@@ -55,7 +54,7 @@ export const ArrayInput = (props: ArrayInputProps) => {
|
|
|
55
54
|
{filteredVariants.map((variant) => {
|
|
56
55
|
return (
|
|
57
56
|
<Button
|
|
58
|
-
key={`${
|
|
57
|
+
key={`${experimentValue}-${variant.id}`}
|
|
59
58
|
text={`Add ${variant.label}`}
|
|
60
59
|
mode="ghost"
|
|
61
60
|
disabled={usedVariants?.includes(variant.id)}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import equal from 'fast-deep-equal'
|
|
2
|
-
import {createContext, useContext, useMemo
|
|
2
|
+
import {createContext, useContext, useMemo} from 'react'
|
|
3
3
|
import {type ObjectInputProps, useClient, useWorkspace} from 'sanity'
|
|
4
4
|
import {suspend} from 'suspend-react'
|
|
5
5
|
|
|
@@ -11,13 +11,16 @@ import {ExperimentContextProps, FieldPluginConfig} from '../types'
|
|
|
11
11
|
export const CONFIG_DEFAULT = {
|
|
12
12
|
fields: [],
|
|
13
13
|
apiVersion: '2024-11-07',
|
|
14
|
+
experimentNameOverride: 'experiment',
|
|
15
|
+
variantNameOverride: 'variant',
|
|
16
|
+
variantId: 'variantId',
|
|
17
|
+
variantArrayName: 'variants',
|
|
18
|
+
experimentId: 'experimentId',
|
|
14
19
|
}
|
|
15
20
|
|
|
16
21
|
export const ExperimentContext = createContext<ExperimentContextProps>({
|
|
17
22
|
...CONFIG_DEFAULT,
|
|
18
23
|
experiments: [],
|
|
19
|
-
setSecret: () => undefined,
|
|
20
|
-
secret: undefined,
|
|
21
24
|
})
|
|
22
25
|
|
|
23
26
|
export function useExperimentContext() {
|
|
@@ -30,7 +33,6 @@ type ExperimentProps = ObjectInputProps & {
|
|
|
30
33
|
|
|
31
34
|
export function ExperimentProvider(props: ExperimentProps) {
|
|
32
35
|
const {experimentFieldPluginConfig} = props
|
|
33
|
-
const [secret, setSecret] = useState<string | undefined>()
|
|
34
36
|
|
|
35
37
|
const client = useClient({apiVersion: experimentFieldPluginConfig.apiVersion})
|
|
36
38
|
const workspace = useWorkspace()
|
|
@@ -42,17 +44,17 @@ export function ExperimentProvider(props: ExperimentProps) {
|
|
|
42
44
|
// eslint-disable-next-line require-await
|
|
43
45
|
async () => {
|
|
44
46
|
if (typeof experimentFieldPluginConfig.experiments === 'function') {
|
|
45
|
-
return experimentFieldPluginConfig.experiments(client
|
|
47
|
+
return experimentFieldPluginConfig.experiments(client)
|
|
46
48
|
}
|
|
47
49
|
return experimentFieldPluginConfig.experiments
|
|
48
50
|
},
|
|
49
|
-
[workspace
|
|
51
|
+
[workspace],
|
|
50
52
|
{equal},
|
|
51
53
|
)
|
|
52
54
|
|
|
53
55
|
const context = useMemo(
|
|
54
|
-
() => ({...experimentFieldPluginConfig, experiments
|
|
55
|
-
[experimentFieldPluginConfig, experiments
|
|
56
|
+
() => ({...experimentFieldPluginConfig, experiments}),
|
|
57
|
+
[experimentFieldPluginConfig, experiments],
|
|
56
58
|
)
|
|
57
59
|
|
|
58
60
|
return (
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import {CloseIcon} from '@sanity/icons'
|
|
2
|
-
import {useCallback, useMemo} from 'react'
|
|
3
2
|
import {GiSoapExperiment} from 'react-icons/gi'
|
|
4
3
|
import {
|
|
5
4
|
defineDocumentFieldAction,
|
|
@@ -14,71 +13,104 @@ import {
|
|
|
14
13
|
type PatchStuff = {onChange: (patch: FormPatch | FormPatch[] | PatchEvent) => void; inputId: string}
|
|
15
14
|
|
|
16
15
|
const useAddExperimentAction = (
|
|
17
|
-
props: DocumentFieldActionProps &
|
|
16
|
+
props: DocumentFieldActionProps &
|
|
17
|
+
PatchStuff & {experimentNameOverride: string; experimentId: string; active: boolean},
|
|
18
18
|
): DocumentFieldActionItem => {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}, [patchActiveEvent, props])
|
|
19
|
+
const {onChange, active, experimentNameOverride} = props
|
|
20
|
+
|
|
21
|
+
const handleAddAction = () => {
|
|
22
|
+
onChange([set(!active, ['active'])])
|
|
23
|
+
}
|
|
25
24
|
|
|
26
25
|
return {
|
|
27
|
-
title:
|
|
26
|
+
title: `Add ${experimentNameOverride}`,
|
|
28
27
|
type: 'action',
|
|
29
28
|
icon: GiSoapExperiment,
|
|
30
|
-
onAction:
|
|
29
|
+
onAction: handleAddAction,
|
|
31
30
|
renderAsButton: true,
|
|
32
31
|
}
|
|
33
32
|
}
|
|
34
33
|
|
|
35
34
|
const useRemoveExperimentAction = (
|
|
36
|
-
props: DocumentFieldActionProps &
|
|
35
|
+
props: DocumentFieldActionProps &
|
|
36
|
+
PatchStuff & {experimentNameOverride: string; experimentId: string; active: boolean},
|
|
37
37
|
): DocumentFieldActionItem => {
|
|
38
|
-
const
|
|
38
|
+
const {onChange, active, experimentId, experimentNameOverride} = props
|
|
39
|
+
const patchActiveFalseEvent = () => {
|
|
39
40
|
const activeId = ['active']
|
|
40
|
-
return set(
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
const
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
return set(!active, activeId)
|
|
42
|
+
}
|
|
43
|
+
const patchClearEvent = () => {
|
|
44
|
+
const experiment = [experimentId]
|
|
45
|
+
const variants = [experimentNameOverride]
|
|
46
|
+
return [unset(experiment), unset(variants)]
|
|
47
|
+
}
|
|
48
|
+
const handleClearAction = () => {
|
|
49
|
+
const clearEvents = patchClearEvent()
|
|
50
|
+
const activeEvent = patchActiveFalseEvent()
|
|
51
|
+
onChange([activeEvent, ...clearEvents])
|
|
52
|
+
}
|
|
52
53
|
return {
|
|
53
|
-
title:
|
|
54
|
+
title: `Remove ${experimentNameOverride}`,
|
|
54
55
|
type: 'action',
|
|
55
56
|
icon: CloseIcon,
|
|
56
|
-
onAction:
|
|
57
|
+
onAction: handleClearAction,
|
|
57
58
|
renderAsButton: true,
|
|
58
59
|
}
|
|
59
60
|
}
|
|
60
61
|
|
|
61
|
-
const newActions = ({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
const newActions = ({
|
|
63
|
+
onChange,
|
|
64
|
+
inputId,
|
|
65
|
+
active,
|
|
66
|
+
experimentNameOverride,
|
|
67
|
+
experimentId,
|
|
68
|
+
}: PatchStuff & {active?: boolean; experimentNameOverride: string; experimentId: string}) => {
|
|
69
|
+
const removeAction = defineDocumentFieldAction({
|
|
70
|
+
name: `Remove ${experimentNameOverride}`,
|
|
71
|
+
useAction: (props) =>
|
|
72
|
+
useRemoveExperimentAction({
|
|
73
|
+
...props,
|
|
74
|
+
active: true,
|
|
75
|
+
onChange,
|
|
76
|
+
inputId,
|
|
77
|
+
experimentNameOverride,
|
|
78
|
+
experimentId,
|
|
79
|
+
}),
|
|
80
|
+
})
|
|
81
|
+
const addAction = defineDocumentFieldAction({
|
|
82
|
+
name: `Add ${experimentNameOverride}`,
|
|
83
|
+
useAction: (props) =>
|
|
84
|
+
useAddExperimentAction({
|
|
85
|
+
...props,
|
|
86
|
+
active: false,
|
|
87
|
+
onChange,
|
|
88
|
+
inputId,
|
|
89
|
+
experimentNameOverride,
|
|
90
|
+
experimentId,
|
|
91
|
+
}),
|
|
92
|
+
})
|
|
93
|
+
if (active) {
|
|
94
|
+
return removeAction
|
|
95
|
+
}
|
|
96
|
+
return addAction
|
|
97
|
+
}
|
|
71
98
|
|
|
72
|
-
export const ExperimentField = (
|
|
99
|
+
export const ExperimentField = (
|
|
100
|
+
props: ObjectFieldProps & {experimentNameOverride: string; experimentId: string},
|
|
101
|
+
) => {
|
|
73
102
|
const {onChange} = props.inputProps
|
|
74
|
-
const {inputId} = props
|
|
103
|
+
const {inputId, experimentNameOverride, experimentId} = props
|
|
75
104
|
const active = props.value?.active as boolean | undefined
|
|
76
105
|
|
|
77
106
|
const oldActions = props.actions || []
|
|
78
107
|
|
|
79
108
|
const withActionProps = {
|
|
80
109
|
...props,
|
|
81
|
-
actions: [
|
|
110
|
+
actions: [
|
|
111
|
+
newActions({onChange, inputId, active, experimentNameOverride, experimentId}),
|
|
112
|
+
...oldActions,
|
|
113
|
+
],
|
|
82
114
|
}
|
|
83
115
|
return props.renderDefault(withActionProps)
|
|
84
116
|
}
|
|
@@ -20,11 +20,14 @@ const formatlistOptions = (experiments: ExperimentType[]): SelectOption[] =>
|
|
|
20
20
|
value: experiment.id,
|
|
21
21
|
}))
|
|
22
22
|
|
|
23
|
-
export const ExperimentInput = (props: StringInputProps) => {
|
|
23
|
+
export const ExperimentInput = (props: StringInputProps & {variantNameOverride: string}) => {
|
|
24
24
|
const {experiments} = useExperimentContext()
|
|
25
25
|
|
|
26
26
|
const id = useFormValue(['_id']) as string
|
|
27
|
-
const aditionalChangePath = useMemo(
|
|
27
|
+
const aditionalChangePath = useMemo(
|
|
28
|
+
() => [...props.path.slice(0, -1), props.variantNameOverride],
|
|
29
|
+
[props.variantNameOverride, props.path],
|
|
30
|
+
)
|
|
28
31
|
const subValues = useFormValue(aditionalChangePath)
|
|
29
32
|
|
|
30
33
|
const {patch} = useDocumentOperation(id.replace('drafts.', ''), props.schemaType.name)
|
|
@@ -1,71 +1,18 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
useFormValue,
|
|
10
|
-
} from 'sanity'
|
|
11
|
-
|
|
12
|
-
import {VariantType} from '../types'
|
|
13
|
-
import {useExperimentContext} from './ExperimentContext'
|
|
14
|
-
import {Select} from './Select'
|
|
15
|
-
|
|
16
|
-
const formatlistOptions = (varants: VariantType[]) =>
|
|
17
|
-
varants.map((variant) => ({
|
|
18
|
-
title: variant.label,
|
|
19
|
-
value: variant.id,
|
|
20
|
-
}))
|
|
21
|
-
|
|
22
|
-
export const VariantInput = (props: StringInputProps) => {
|
|
23
|
-
const experimentPath = props.path.slice(0, -3)
|
|
24
|
-
|
|
25
|
-
const experimentValue = useFormValue([...experimentPath, 'experimentValue'])
|
|
26
|
-
|
|
27
|
-
const id = useFormValue(['_id']) as string
|
|
28
|
-
|
|
29
|
-
const {patch} = useDocumentOperation(id.replace('drafts.', ''), props.schemaType.name)
|
|
30
|
-
|
|
31
|
-
const {experiments} = useExperimentContext()
|
|
32
|
-
|
|
33
|
-
const handleChange = useCallback(
|
|
34
|
-
(
|
|
35
|
-
event: FormEvent<Element>,
|
|
36
|
-
onChange: (patchchange: FormPatch | FormPatch[] | PatchEvent) => void,
|
|
37
|
-
) => {
|
|
38
|
-
const target = event.currentTarget as HTMLSelectElement
|
|
39
|
-
const inputValue = target.value
|
|
40
|
-
const variantExperimentId = props.id.replace('variantId', 'experimentId')
|
|
41
|
-
|
|
42
|
-
if (inputValue) {
|
|
43
|
-
onChange(set(inputValue))
|
|
44
|
-
const patchEvent = {
|
|
45
|
-
set: {[variantExperimentId]: experimentValue},
|
|
46
|
-
}
|
|
47
|
-
patch.execute([patchEvent])
|
|
48
|
-
} else {
|
|
49
|
-
onChange(unset())
|
|
50
|
-
const patchEvent = {
|
|
51
|
-
unset: [variantExperimentId],
|
|
52
|
-
}
|
|
53
|
-
patch.execute([patchEvent])
|
|
54
|
-
}
|
|
55
|
-
},
|
|
56
|
-
[experimentValue, patch, props.id],
|
|
57
|
-
)
|
|
58
|
-
|
|
59
|
-
const filteredVariants =
|
|
60
|
-
experiments.find((option) => {
|
|
61
|
-
return option.id === experimentValue
|
|
62
|
-
})?.variants || []
|
|
63
|
-
|
|
1
|
+
import {Button, Inline, Stack} from '@sanity/ui'
|
|
2
|
+
import {ObjectInputProps, set, useFormValue} from 'sanity'
|
|
3
|
+
|
|
4
|
+
export const VariantInput = (props: ObjectInputProps) => {
|
|
5
|
+
const defaultValue = useFormValue([props.path[0], 'default'])
|
|
6
|
+
const handleClick = () => {
|
|
7
|
+
props.onChange(set(defaultValue, ['value']))
|
|
8
|
+
}
|
|
64
9
|
return (
|
|
65
|
-
<
|
|
66
|
-
{
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
10
|
+
<Stack space={3}>
|
|
11
|
+
{props.renderDefault(props)}
|
|
12
|
+
|
|
13
|
+
<Inline space={1}>
|
|
14
|
+
<Button text="Copy default" mode="ghost" onClick={() => handleClick()} />
|
|
15
|
+
</Inline>
|
|
16
|
+
</Stack>
|
|
70
17
|
)
|
|
71
18
|
}
|
package/src/fieldExperiments.tsx
CHANGED
|
@@ -5,34 +5,47 @@ import {
|
|
|
5
5
|
defineType,
|
|
6
6
|
FieldDefinition,
|
|
7
7
|
isObjectInputProps,
|
|
8
|
-
SanityClient,
|
|
9
8
|
} from 'sanity'
|
|
10
9
|
|
|
11
10
|
import {ArrayInput} from './components/Array'
|
|
12
11
|
import {CONFIG_DEFAULT, ExperimentProvider} from './components/ExperimentContext'
|
|
13
12
|
import {ExperimentField} from './components/ExperimentField'
|
|
14
13
|
import {ExperimentInput} from './components/ExperimentInput'
|
|
14
|
+
import {VariantInput} from './components/VariantInput'
|
|
15
15
|
import {VariantPreview} from './components/VariantPreview'
|
|
16
|
-
import {
|
|
16
|
+
import {FieldPluginConfig} from './types'
|
|
17
17
|
import {flattenSchemaType} from './utils/flattenSchemaType'
|
|
18
18
|
|
|
19
|
-
const
|
|
19
|
+
const createExperimentType = ({
|
|
20
20
|
field,
|
|
21
|
+
experimentNameOverride,
|
|
22
|
+
variantNameOverride,
|
|
23
|
+
variantId,
|
|
24
|
+
variantArrayName,
|
|
25
|
+
experimentId,
|
|
21
26
|
}: {
|
|
22
27
|
field: string | FieldDefinition
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
experimentNameOverride: string
|
|
29
|
+
variantNameOverride: string
|
|
30
|
+
variantId: string
|
|
31
|
+
variantArrayName: string
|
|
32
|
+
experimentId: string
|
|
26
33
|
}) => {
|
|
27
34
|
const typeName = typeof field === `string` ? field : field.name
|
|
28
35
|
const usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1)
|
|
29
|
-
const
|
|
36
|
+
const variantName = `${variantNameOverride}${usedName}`
|
|
30
37
|
|
|
31
38
|
return defineType({
|
|
32
|
-
name:
|
|
39
|
+
name: `${experimentNameOverride}${usedName}`,
|
|
33
40
|
type: 'object',
|
|
34
41
|
components: {
|
|
35
|
-
field:
|
|
42
|
+
field: (props) => (
|
|
43
|
+
<ExperimentField
|
|
44
|
+
{...props}
|
|
45
|
+
experimentId={experimentId}
|
|
46
|
+
experimentNameOverride={experimentNameOverride}
|
|
47
|
+
/>
|
|
48
|
+
),
|
|
36
49
|
},
|
|
37
50
|
fields: [
|
|
38
51
|
typeof field === `string`
|
|
@@ -52,31 +65,37 @@ const createFieldType = ({
|
|
|
52
65
|
hidden: true,
|
|
53
66
|
}),
|
|
54
67
|
defineField({
|
|
55
|
-
|
|
56
|
-
name: 'experimentId',
|
|
68
|
+
name: experimentId,
|
|
57
69
|
type: 'string',
|
|
58
70
|
components: {
|
|
59
|
-
input:
|
|
71
|
+
input: (props) => (
|
|
72
|
+
<ExperimentInput {...props} variantNameOverride={variantNameOverride} />
|
|
73
|
+
),
|
|
60
74
|
},
|
|
61
75
|
hidden: ({parent}) => {
|
|
62
76
|
return !parent?.active
|
|
63
77
|
},
|
|
64
78
|
}),
|
|
65
79
|
defineField({
|
|
66
|
-
name:
|
|
80
|
+
name: variantArrayName,
|
|
67
81
|
type: 'array',
|
|
68
82
|
hidden: ({parent}) => {
|
|
69
|
-
return !parent?.experimentId
|
|
83
|
+
return !parent?.[experimentId]
|
|
70
84
|
},
|
|
71
85
|
components: {
|
|
72
86
|
input: (props: ArrayOfObjectsInputProps) => (
|
|
73
|
-
<ArrayInput
|
|
87
|
+
<ArrayInput
|
|
88
|
+
{...props}
|
|
89
|
+
variantName={variantName}
|
|
90
|
+
variantId={variantId}
|
|
91
|
+
experimentId={experimentId}
|
|
92
|
+
/>
|
|
74
93
|
),
|
|
75
94
|
},
|
|
76
95
|
of: [
|
|
77
96
|
defineField({
|
|
78
|
-
name:
|
|
79
|
-
type:
|
|
97
|
+
name: variantName,
|
|
98
|
+
type: variantName,
|
|
80
99
|
}),
|
|
81
100
|
],
|
|
82
101
|
}),
|
|
@@ -84,30 +103,36 @@ const createFieldType = ({
|
|
|
84
103
|
})
|
|
85
104
|
}
|
|
86
105
|
|
|
87
|
-
const
|
|
106
|
+
const createVariantType = ({
|
|
88
107
|
field,
|
|
108
|
+
variantNameOverride,
|
|
109
|
+
variantId,
|
|
110
|
+
experimentId,
|
|
89
111
|
}: {
|
|
90
112
|
field: string | FieldDefinition
|
|
91
|
-
|
|
113
|
+
variantNameOverride: string
|
|
114
|
+
variantId: string
|
|
115
|
+
experimentId: string
|
|
92
116
|
}) => {
|
|
93
117
|
const typeName = typeof field === `string` ? field : field.name
|
|
94
118
|
const usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1)
|
|
95
119
|
return defineType({
|
|
96
|
-
name:
|
|
97
|
-
title:
|
|
120
|
+
name: `${variantNameOverride}${usedName}`,
|
|
121
|
+
title: `${variantNameOverride} array ${usedName}`,
|
|
98
122
|
type: 'object',
|
|
99
123
|
components: {
|
|
100
124
|
preview: VariantPreview,
|
|
125
|
+
input: VariantInput,
|
|
101
126
|
},
|
|
102
127
|
fields: [
|
|
103
128
|
{
|
|
104
129
|
type: 'string',
|
|
105
|
-
name:
|
|
130
|
+
name: variantId,
|
|
106
131
|
readOnly: true,
|
|
107
132
|
},
|
|
108
133
|
{
|
|
109
134
|
type: 'string',
|
|
110
|
-
name:
|
|
135
|
+
name: experimentId,
|
|
111
136
|
hidden: true,
|
|
112
137
|
},
|
|
113
138
|
typeof field === `string`
|
|
@@ -115,36 +140,66 @@ const createFieldObjectType = ({
|
|
|
115
140
|
defineField({
|
|
116
141
|
name: 'value',
|
|
117
142
|
type: field,
|
|
118
|
-
hidden: ({parent}) => !parent?.
|
|
143
|
+
// hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
|
|
119
144
|
})
|
|
120
145
|
: // Pass in the configured options, but overwrite the name
|
|
121
146
|
{
|
|
122
147
|
...field,
|
|
123
148
|
name: 'value',
|
|
124
|
-
hidden: ({parent}) => !parent?.
|
|
149
|
+
// hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
|
|
125
150
|
},
|
|
126
151
|
],
|
|
127
152
|
preview: {
|
|
128
153
|
select: {
|
|
129
|
-
variant:
|
|
130
|
-
experiment:
|
|
154
|
+
variant: variantId,
|
|
155
|
+
experiment: experimentId,
|
|
131
156
|
value: 'value',
|
|
132
157
|
},
|
|
133
158
|
},
|
|
134
159
|
})
|
|
135
160
|
}
|
|
136
161
|
|
|
137
|
-
const fieldSchema = ({
|
|
162
|
+
const fieldSchema = ({
|
|
163
|
+
fields,
|
|
164
|
+
experimentNameOverride,
|
|
165
|
+
variantNameOverride,
|
|
166
|
+
variantId,
|
|
167
|
+
variantArrayName,
|
|
168
|
+
experimentId,
|
|
169
|
+
}: Required<Omit<FieldPluginConfig, 'apiVersion' | 'experiments'>>) => {
|
|
138
170
|
return [
|
|
139
|
-
...fields.map((field) =>
|
|
140
|
-
|
|
171
|
+
...fields.map((field) =>
|
|
172
|
+
createVariantType({field, variantNameOverride, variantId, experimentId}),
|
|
173
|
+
),
|
|
174
|
+
...fields.map((field) =>
|
|
175
|
+
createExperimentType({
|
|
176
|
+
field,
|
|
177
|
+
experimentNameOverride,
|
|
178
|
+
variantNameOverride,
|
|
179
|
+
variantId,
|
|
180
|
+
variantArrayName,
|
|
181
|
+
experimentId,
|
|
182
|
+
}),
|
|
183
|
+
),
|
|
141
184
|
]
|
|
142
185
|
}
|
|
143
186
|
|
|
144
187
|
export const fieldLevelExperiments = definePlugin<FieldPluginConfig>((config) => {
|
|
145
188
|
const pluginConfig = {...CONFIG_DEFAULT, ...config}
|
|
146
|
-
const {fields,
|
|
147
|
-
|
|
189
|
+
const {fields, experimentNameOverride, variantNameOverride} = pluginConfig
|
|
190
|
+
|
|
191
|
+
const experimentId = `${experimentNameOverride}Id`
|
|
192
|
+
const variantArrayName = `${variantNameOverride}s`
|
|
193
|
+
const variantId = `${variantNameOverride}Id`
|
|
194
|
+
|
|
195
|
+
const fieldSchemaConfig = fieldSchema({
|
|
196
|
+
fields,
|
|
197
|
+
experimentNameOverride,
|
|
198
|
+
variantNameOverride,
|
|
199
|
+
variantId,
|
|
200
|
+
variantArrayName,
|
|
201
|
+
experimentId,
|
|
202
|
+
})
|
|
148
203
|
return {
|
|
149
204
|
name: 'sanity-personalistaion-plugin-field-level-experiments',
|
|
150
205
|
schema: {
|
|
@@ -162,12 +217,22 @@ export const fieldLevelExperiments = definePlugin<FieldPluginConfig>((config) =>
|
|
|
162
217
|
const flatFieldTypeNames = flattenSchemaType(props.schemaType).map(
|
|
163
218
|
(field) => field.type.name,
|
|
164
219
|
)
|
|
165
|
-
const hasExperiment = flatFieldTypeNames.some((name) =>
|
|
220
|
+
const hasExperiment = flatFieldTypeNames.some((name) =>
|
|
221
|
+
name.startsWith(experimentNameOverride),
|
|
222
|
+
)
|
|
166
223
|
|
|
167
224
|
if (!hasExperiment) {
|
|
168
225
|
return props.renderDefault(props)
|
|
169
226
|
}
|
|
170
|
-
const providerProps = {
|
|
227
|
+
const providerProps = {
|
|
228
|
+
...props,
|
|
229
|
+
experimentFieldPluginConfig: {
|
|
230
|
+
...pluginConfig,
|
|
231
|
+
variantId,
|
|
232
|
+
variantArrayName,
|
|
233
|
+
experimentId,
|
|
234
|
+
},
|
|
235
|
+
}
|
|
171
236
|
return ExperimentProvider(providerProps)
|
|
172
237
|
},
|
|
173
238
|
},
|
package/src/index.ts
CHANGED