@sanity/personalization-plugin 2.1.0-growthbook.1 → 2.2.0-growthbook.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 +60 -4
- package/dist/index.d.mts +19 -10
- package/dist/index.d.ts +19 -10
- package/dist/index.js +175 -66
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +175 -66
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/components/Array.tsx +11 -12
- package/src/components/ExperimentContext.tsx +5 -0
- package/src/components/ExperimentField.tsx +97 -41
- package/src/components/ExperimentInput.tsx +6 -3
- package/src/components/VariantInput.tsx +15 -68
- package/src/fieldExperiments.tsx +100 -34
- package/src/types.ts +19 -10
package/package.json
CHANGED
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)}
|
|
@@ -11,6 +11,11 @@ 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>({
|
|
@@ -14,71 +14,127 @@ import {
|
|
|
14
14
|
type PatchStuff = {onChange: (patch: FormPatch | FormPatch[] | PatchEvent) => void; inputId: string}
|
|
15
15
|
|
|
16
16
|
const useAddExperimentAction = (
|
|
17
|
-
props: DocumentFieldActionProps &
|
|
17
|
+
props: DocumentFieldActionProps &
|
|
18
|
+
PatchStuff & {experimentNameOverride: string; experimentId: string; active: boolean},
|
|
18
19
|
): DocumentFieldActionItem => {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}, [patchActiveEvent, props])
|
|
20
|
+
const {onChange, active, experimentNameOverride} = props
|
|
21
|
+
|
|
22
|
+
const handleAddAction = useCallback(() => {
|
|
23
|
+
onChange([set(!active, ['active'])])
|
|
24
|
+
}, [onChange, active])
|
|
25
25
|
|
|
26
26
|
return {
|
|
27
|
-
title:
|
|
27
|
+
title: `Add ${experimentNameOverride}`,
|
|
28
28
|
type: 'action',
|
|
29
29
|
icon: GiSoapExperiment,
|
|
30
|
-
onAction:
|
|
30
|
+
onAction: handleAddAction,
|
|
31
31
|
renderAsButton: true,
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
const useRemoveExperimentAction = (
|
|
36
|
-
props: DocumentFieldActionProps &
|
|
36
|
+
props: DocumentFieldActionProps &
|
|
37
|
+
PatchStuff & {
|
|
38
|
+
experimentNameOverride: string
|
|
39
|
+
experimentId: string
|
|
40
|
+
active: boolean
|
|
41
|
+
variantNameOverride: string
|
|
42
|
+
},
|
|
37
43
|
): DocumentFieldActionItem => {
|
|
38
|
-
const
|
|
39
|
-
const activeId = ['active']
|
|
40
|
-
return set(false, activeId)
|
|
41
|
-
}, [])
|
|
44
|
+
const {onChange, active, experimentId, experimentNameOverride, variantNameOverride} = props
|
|
42
45
|
|
|
43
|
-
const
|
|
44
|
-
const
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
props.onChange([patchActiveEvent, ...patchClearEvent])
|
|
50
|
-
}, [patchActiveEvent, patchClearEvent, props])
|
|
46
|
+
const handleClearAction = useCallback(() => {
|
|
47
|
+
const activeId = ['active']
|
|
48
|
+
const experiment = [experimentId]
|
|
49
|
+
const variants = [`${variantNameOverride}s`]
|
|
50
|
+
onChange([set(!active, activeId), unset(experiment), unset(variants)])
|
|
51
|
+
}, [onChange, active, experimentId, variantNameOverride])
|
|
51
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
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
62
|
+
const createActions = ({
|
|
63
|
+
onChange,
|
|
64
|
+
inputId,
|
|
65
|
+
active,
|
|
66
|
+
experimentNameOverride,
|
|
67
|
+
experimentId,
|
|
68
|
+
variantNameOverride,
|
|
69
|
+
}: PatchStuff & {
|
|
70
|
+
active?: boolean
|
|
71
|
+
experimentNameOverride: string
|
|
72
|
+
experimentId: string
|
|
73
|
+
variantNameOverride: string
|
|
74
|
+
}) => {
|
|
75
|
+
const removeAction = defineDocumentFieldAction({
|
|
76
|
+
name: `Remove ${experimentNameOverride}`,
|
|
77
|
+
useAction: (props) =>
|
|
78
|
+
useRemoveExperimentAction({
|
|
79
|
+
...props,
|
|
80
|
+
active: true,
|
|
81
|
+
onChange,
|
|
82
|
+
inputId,
|
|
83
|
+
experimentNameOverride,
|
|
84
|
+
experimentId,
|
|
85
|
+
variantNameOverride,
|
|
86
|
+
}),
|
|
87
|
+
})
|
|
88
|
+
const addAction = defineDocumentFieldAction({
|
|
89
|
+
name: `Add ${experimentNameOverride}`,
|
|
90
|
+
useAction: (props) =>
|
|
91
|
+
useAddExperimentAction({
|
|
92
|
+
...props,
|
|
93
|
+
active: false,
|
|
94
|
+
onChange,
|
|
95
|
+
inputId,
|
|
96
|
+
experimentNameOverride,
|
|
97
|
+
experimentId,
|
|
98
|
+
}),
|
|
99
|
+
})
|
|
100
|
+
return active ? removeAction : addAction
|
|
101
|
+
}
|
|
71
102
|
|
|
72
|
-
export const ExperimentField = (
|
|
103
|
+
export const ExperimentField = (
|
|
104
|
+
props: ObjectFieldProps & {
|
|
105
|
+
experimentNameOverride: string
|
|
106
|
+
experimentId: string
|
|
107
|
+
variantNameOverride: string
|
|
108
|
+
},
|
|
109
|
+
) => {
|
|
73
110
|
const {onChange} = props.inputProps
|
|
74
|
-
const {inputId} = props
|
|
111
|
+
const {inputId, experimentNameOverride, experimentId, variantNameOverride} = props
|
|
75
112
|
const active = props.value?.active as boolean | undefined
|
|
76
113
|
|
|
77
|
-
const
|
|
114
|
+
const actionProps = useMemo(
|
|
115
|
+
() => ({
|
|
116
|
+
onChange,
|
|
117
|
+
inputId,
|
|
118
|
+
active,
|
|
119
|
+
experimentNameOverride,
|
|
120
|
+
experimentId,
|
|
121
|
+
variantNameOverride,
|
|
122
|
+
}),
|
|
123
|
+
[onChange, inputId, active, experimentNameOverride, experimentId, variantNameOverride],
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
const memoizedActions = useMemo(() => {
|
|
127
|
+
const oldActions = props.actions || []
|
|
128
|
+
return [createActions(actionProps), ...oldActions]
|
|
129
|
+
}, [actionProps, props.actions])
|
|
130
|
+
|
|
131
|
+
const withActionProps = useMemo(
|
|
132
|
+
() => ({
|
|
133
|
+
...props,
|
|
134
|
+
actions: memoizedActions,
|
|
135
|
+
}),
|
|
136
|
+
[props, memoizedActions],
|
|
137
|
+
)
|
|
78
138
|
|
|
79
|
-
const withActionProps = {
|
|
80
|
-
...props,
|
|
81
|
-
actions: [newActions({onChange, inputId, active}), ...oldActions],
|
|
82
|
-
}
|
|
83
139
|
return props.renderDefault(withActionProps)
|
|
84
140
|
}
|
|
@@ -20,13 +20,16 @@ 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(
|
|
28
|
-
|
|
27
|
+
const aditionalChangePath = useMemo(
|
|
28
|
+
() => [...props.path.slice(0, -1), `${props.variantNameOverride}s`],
|
|
29
|
+
[props.variantNameOverride, props.path],
|
|
30
|
+
)
|
|
29
31
|
|
|
32
|
+
const subValues = useFormValue(aditionalChangePath)
|
|
30
33
|
const {patch} = useDocumentOperation(id.replace('drafts.', ''), props.schemaType.name)
|
|
31
34
|
|
|
32
35
|
const handleChange = useCallback(
|
|
@@ -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,48 @@ 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
|
+
variantNameOverride={variantNameOverride}
|
|
48
|
+
/>
|
|
49
|
+
),
|
|
36
50
|
},
|
|
37
51
|
fields: [
|
|
38
52
|
typeof field === `string`
|
|
@@ -52,31 +66,37 @@ const createFieldType = ({
|
|
|
52
66
|
hidden: true,
|
|
53
67
|
}),
|
|
54
68
|
defineField({
|
|
55
|
-
|
|
56
|
-
name: 'experimentId',
|
|
69
|
+
name: experimentId,
|
|
57
70
|
type: 'string',
|
|
58
71
|
components: {
|
|
59
|
-
input:
|
|
72
|
+
input: (props) => (
|
|
73
|
+
<ExperimentInput {...props} variantNameOverride={variantNameOverride} />
|
|
74
|
+
),
|
|
60
75
|
},
|
|
61
76
|
hidden: ({parent}) => {
|
|
62
77
|
return !parent?.active
|
|
63
78
|
},
|
|
64
79
|
}),
|
|
65
80
|
defineField({
|
|
66
|
-
name:
|
|
81
|
+
name: variantArrayName,
|
|
67
82
|
type: 'array',
|
|
68
83
|
hidden: ({parent}) => {
|
|
69
|
-
return !parent?.experimentId
|
|
84
|
+
return !parent?.[experimentId]
|
|
70
85
|
},
|
|
71
86
|
components: {
|
|
72
87
|
input: (props: ArrayOfObjectsInputProps) => (
|
|
73
|
-
<ArrayInput
|
|
88
|
+
<ArrayInput
|
|
89
|
+
{...props}
|
|
90
|
+
variantName={variantName}
|
|
91
|
+
variantId={variantId}
|
|
92
|
+
experimentId={experimentId}
|
|
93
|
+
/>
|
|
74
94
|
),
|
|
75
95
|
},
|
|
76
96
|
of: [
|
|
77
97
|
defineField({
|
|
78
|
-
name:
|
|
79
|
-
type:
|
|
98
|
+
name: variantName,
|
|
99
|
+
type: variantName,
|
|
80
100
|
}),
|
|
81
101
|
],
|
|
82
102
|
}),
|
|
@@ -84,30 +104,36 @@ const createFieldType = ({
|
|
|
84
104
|
})
|
|
85
105
|
}
|
|
86
106
|
|
|
87
|
-
const
|
|
107
|
+
const createVariantType = ({
|
|
88
108
|
field,
|
|
109
|
+
variantNameOverride,
|
|
110
|
+
variantId,
|
|
111
|
+
experimentId,
|
|
89
112
|
}: {
|
|
90
113
|
field: string | FieldDefinition
|
|
91
|
-
|
|
114
|
+
variantNameOverride: string
|
|
115
|
+
variantId: string
|
|
116
|
+
experimentId: string
|
|
92
117
|
}) => {
|
|
93
118
|
const typeName = typeof field === `string` ? field : field.name
|
|
94
119
|
const usedName = String(typeName[0]).toUpperCase() + String(typeName).slice(1)
|
|
95
120
|
return defineType({
|
|
96
|
-
name:
|
|
97
|
-
title:
|
|
121
|
+
name: `${variantNameOverride}${usedName}`,
|
|
122
|
+
title: `${variantNameOverride} array ${usedName}`,
|
|
98
123
|
type: 'object',
|
|
99
124
|
components: {
|
|
100
125
|
preview: VariantPreview,
|
|
126
|
+
input: VariantInput,
|
|
101
127
|
},
|
|
102
128
|
fields: [
|
|
103
129
|
{
|
|
104
130
|
type: 'string',
|
|
105
|
-
name:
|
|
131
|
+
name: variantId,
|
|
106
132
|
readOnly: true,
|
|
107
133
|
},
|
|
108
134
|
{
|
|
109
135
|
type: 'string',
|
|
110
|
-
name:
|
|
136
|
+
name: experimentId,
|
|
111
137
|
hidden: true,
|
|
112
138
|
},
|
|
113
139
|
typeof field === `string`
|
|
@@ -115,36 +141,66 @@ const createFieldObjectType = ({
|
|
|
115
141
|
defineField({
|
|
116
142
|
name: 'value',
|
|
117
143
|
type: field,
|
|
118
|
-
hidden: ({parent}) => !parent?.
|
|
144
|
+
// hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
|
|
119
145
|
})
|
|
120
146
|
: // Pass in the configured options, but overwrite the name
|
|
121
147
|
{
|
|
122
148
|
...field,
|
|
123
149
|
name: 'value',
|
|
124
|
-
hidden: ({parent}) => !parent?.
|
|
150
|
+
// hidden: ({parent}) => !parent?.[`${objectNameOverride}Id`],
|
|
125
151
|
},
|
|
126
152
|
],
|
|
127
153
|
preview: {
|
|
128
154
|
select: {
|
|
129
|
-
variant:
|
|
130
|
-
experiment:
|
|
155
|
+
variant: variantId,
|
|
156
|
+
experiment: experimentId,
|
|
131
157
|
value: 'value',
|
|
132
158
|
},
|
|
133
159
|
},
|
|
134
160
|
})
|
|
135
161
|
}
|
|
136
162
|
|
|
137
|
-
const fieldSchema = ({
|
|
163
|
+
const fieldSchema = ({
|
|
164
|
+
fields,
|
|
165
|
+
experimentNameOverride,
|
|
166
|
+
variantNameOverride,
|
|
167
|
+
variantId,
|
|
168
|
+
variantArrayName,
|
|
169
|
+
experimentId,
|
|
170
|
+
}: Required<Omit<FieldPluginConfig, 'apiVersion' | 'experiments'>>) => {
|
|
138
171
|
return [
|
|
139
|
-
...fields.map((field) =>
|
|
140
|
-
|
|
172
|
+
...fields.map((field) =>
|
|
173
|
+
createVariantType({field, variantNameOverride, variantId, experimentId}),
|
|
174
|
+
),
|
|
175
|
+
...fields.map((field) =>
|
|
176
|
+
createExperimentType({
|
|
177
|
+
field,
|
|
178
|
+
experimentNameOverride,
|
|
179
|
+
variantNameOverride,
|
|
180
|
+
variantId,
|
|
181
|
+
variantArrayName,
|
|
182
|
+
experimentId,
|
|
183
|
+
}),
|
|
184
|
+
),
|
|
141
185
|
]
|
|
142
186
|
}
|
|
143
187
|
|
|
144
188
|
export const fieldLevelExperiments = definePlugin<FieldPluginConfig>((config) => {
|
|
145
189
|
const pluginConfig = {...CONFIG_DEFAULT, ...config}
|
|
146
|
-
const {fields,
|
|
147
|
-
|
|
190
|
+
const {fields, experimentNameOverride, variantNameOverride} = pluginConfig
|
|
191
|
+
|
|
192
|
+
const experimentId = `${experimentNameOverride}Id`
|
|
193
|
+
const variantArrayName = `${variantNameOverride}s`
|
|
194
|
+
const variantId = `${variantNameOverride}Id`
|
|
195
|
+
|
|
196
|
+
const fieldSchemaConfig = fieldSchema({
|
|
197
|
+
fields,
|
|
198
|
+
experimentNameOverride,
|
|
199
|
+
variantNameOverride,
|
|
200
|
+
variantId,
|
|
201
|
+
variantArrayName,
|
|
202
|
+
experimentId,
|
|
203
|
+
})
|
|
148
204
|
return {
|
|
149
205
|
name: 'sanity-personalistaion-plugin-field-level-experiments',
|
|
150
206
|
schema: {
|
|
@@ -162,12 +218,22 @@ export const fieldLevelExperiments = definePlugin<FieldPluginConfig>((config) =>
|
|
|
162
218
|
const flatFieldTypeNames = flattenSchemaType(props.schemaType).map(
|
|
163
219
|
(field) => field.type.name,
|
|
164
220
|
)
|
|
165
|
-
const hasExperiment = flatFieldTypeNames.some((name) =>
|
|
221
|
+
const hasExperiment = flatFieldTypeNames.some((name) =>
|
|
222
|
+
name.startsWith(experimentNameOverride),
|
|
223
|
+
)
|
|
166
224
|
|
|
167
225
|
if (!hasExperiment) {
|
|
168
226
|
return props.renderDefault(props)
|
|
169
227
|
}
|
|
170
|
-
const providerProps = {
|
|
228
|
+
const providerProps = {
|
|
229
|
+
...props,
|
|
230
|
+
experimentFieldPluginConfig: {
|
|
231
|
+
...pluginConfig,
|
|
232
|
+
variantId,
|
|
233
|
+
variantArrayName,
|
|
234
|
+
experimentId,
|
|
235
|
+
},
|
|
236
|
+
}
|
|
171
237
|
return ExperimentProvider(providerProps)
|
|
172
238
|
},
|
|
173
239
|
},
|
package/src/types.ts
CHANGED
|
@@ -25,11 +25,15 @@ export type FieldPluginConfig = {
|
|
|
25
25
|
| ExperimentType[]
|
|
26
26
|
| ((client: SanityClient, secret?: string) => Promise<ExperimentType[]>)
|
|
27
27
|
apiVersion?: string
|
|
28
|
+
experimentNameOverride?: string
|
|
29
|
+
variantNameOverride?: string
|
|
30
|
+
variantId?: string
|
|
31
|
+
variantArrayName?: string
|
|
32
|
+
experimentId?: string
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
export type VariantPreviewProps = Omit<PreviewProps, 'SchemaType'> & {
|
|
31
|
-
|
|
32
|
-
variant: string
|
|
36
|
+
[key: string]: string
|
|
33
37
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
34
38
|
value: any
|
|
35
39
|
}
|
|
@@ -41,15 +45,16 @@ export type ExperimentContextProps = Required<FieldPluginConfig> & {
|
|
|
41
45
|
}
|
|
42
46
|
|
|
43
47
|
export type ArrayInputProps = ArrayOfObjectsInputProps & {
|
|
44
|
-
|
|
48
|
+
variantName: string
|
|
49
|
+
variantId: string
|
|
50
|
+
experimentId: string
|
|
45
51
|
}
|
|
46
52
|
|
|
47
53
|
export type ObjectFieldWithPath = ObjectField<SchemaType> & {path: Path}
|
|
48
54
|
|
|
49
55
|
export type VariantGeneric<T> = {
|
|
56
|
+
[key: string]: string | T | undefined
|
|
50
57
|
_type: string
|
|
51
|
-
variantId?: string
|
|
52
|
-
experimentId?: string
|
|
53
58
|
value?: T
|
|
54
59
|
}
|
|
55
60
|
|
|
@@ -57,11 +62,15 @@ export type ExperimentGeneric<T> = {
|
|
|
57
62
|
_type: string
|
|
58
63
|
default?: T
|
|
59
64
|
experimentValue?: string
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
[key: string]:
|
|
66
|
+
| Array<
|
|
67
|
+
{
|
|
68
|
+
_key: string
|
|
69
|
+
} & VariantGeneric<T>
|
|
70
|
+
>
|
|
71
|
+
| string
|
|
72
|
+
| T
|
|
73
|
+
| undefined
|
|
65
74
|
}
|
|
66
75
|
|
|
67
76
|
export type GrowthbookExperiment = {
|