diff --git a/frontend/__snapshots__/lemon-ui-lemon-input-select--default--dark.png b/frontend/__snapshots__/lemon-ui-lemon-input-select--default--dark.png
index 30d6848c0dc..ec8494f1cb6 100644
Binary files a/frontend/__snapshots__/lemon-ui-lemon-input-select--default--dark.png and b/frontend/__snapshots__/lemon-ui-lemon-input-select--default--dark.png differ
diff --git a/frontend/__snapshots__/lemon-ui-lemon-input-select--default--light.png b/frontend/__snapshots__/lemon-ui-lemon-input-select--default--light.png
index 115f6d83d8d..5496b3c4488 100644
Binary files a/frontend/__snapshots__/lemon-ui-lemon-input-select--default--light.png and b/frontend/__snapshots__/lemon-ui-lemon-input-select--default--light.png differ
diff --git a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--dark.png b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--dark.png
index 4fcefc01064..5a042b0f3b1 100644
Binary files a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--dark.png and b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--dark.png differ
diff --git a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--light.png b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--light.png
index 2e07e4e3103..e10f4cc6a6a 100644
Binary files a/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--light.png and b/frontend/__snapshots__/scenes-app-experiments--complete-funnel-experiment--light.png differ
diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--dark.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--dark.png
index e83f20e9105..32591170a12 100644
Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--dark.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--dark.png differ
diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--light.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--light.png
index d789a9fe7af..8e6538c1e24 100644
Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--light.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment--light.png differ
diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--dark.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--dark.png
index 045db880007..784e386f8b6 100644
Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--dark.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--dark.png differ
diff --git a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--light.png b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--light.png
index e95c74d8a3f..9c8668050ec 100644
Binary files a/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--light.png and b/frontend/__snapshots__/scenes-app-experiments--running-trend-experiment-many-variants--light.png differ
diff --git a/frontend/src/scenes/experiments/ExperimentView/DistributionTable.tsx b/frontend/src/scenes/experiments/ExperimentView/DistributionTable.tsx
index f246bfc1a98..54936f61f0b 100644
--- a/frontend/src/scenes/experiments/ExperimentView/DistributionTable.tsx
+++ b/frontend/src/scenes/experiments/ExperimentView/DistributionTable.tsx
@@ -8,6 +8,7 @@ import { MultivariateFlagVariant } from '~/types'
import { experimentLogic } from '../experimentLogic'
import { VariantTag } from './components'
+import { VariantScreenshot } from './VariantScreenshot'
export function DistributionTable(): JSX.Element {
const { experimentId, experiment, experimentResults } = useValues(experimentLogic)
@@ -32,6 +33,18 @@ export function DistributionTable(): JSX.Element {
return
{`${item.rollout_percentage}%`}
},
},
+ {
+ className: 'w-1/3',
+ key: 'variant_screenshot',
+ title: 'Screenshot',
+ render: function Key(_, item): JSX.Element {
+ return (
+
+
+
+ )
+ },
+ },
]
return (
diff --git a/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx b/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx
new file mode 100644
index 00000000000..c80bcd574ed
--- /dev/null
+++ b/frontend/src/scenes/experiments/ExperimentView/VariantScreenshot.tsx
@@ -0,0 +1,114 @@
+import { IconUpload, IconX } from '@posthog/icons'
+import { LemonButton, LemonDivider, LemonFileInput, LemonModal, LemonSkeleton, lemonToast } from '@posthog/lemon-ui'
+import { useActions, useValues } from 'kea'
+import { useUploadFiles } from 'lib/hooks/useUploadFiles'
+import { useState } from 'react'
+
+import { experimentLogic } from '../experimentLogic'
+import { VariantTag } from './components'
+
+export function VariantScreenshot({
+ variantKey,
+ rolloutPercentage,
+}: {
+ variantKey: string
+ rolloutPercentage: number
+}): JSX.Element {
+ const { experiment } = useValues(experimentLogic)
+ const { updateExperimentVariantImages } = useActions(experimentLogic)
+
+ const [mediaId, setMediaId] = useState(experiment.parameters?.variant_screenshot_media_ids?.[variantKey] || null)
+ const [isLoadingImage, setIsLoadingImage] = useState(true)
+ const [isModalOpen, setIsModalOpen] = useState(false)
+
+ const { setFilesToUpload, filesToUpload, uploading } = useUploadFiles({
+ onUpload: (_, __, id) => {
+ setMediaId(id)
+ if (id) {
+ const updatedVariantImages = {
+ ...experiment.parameters?.variant_screenshot_media_ids,
+ [variantKey]: id,
+ }
+ updateExperimentVariantImages(updatedVariantImages)
+ }
+ },
+ onError: (detail) => {
+ lemonToast.error(`Error uploading image: ${detail}`)
+ },
+ })
+
+ return (
+
+ {!mediaId ? (
+
+
+ Upload a preview of this variant's UI
+ >
+ }
+ />
+ ) : (
+
+
+
setIsModalOpen(true)} className="cursor-zoom-in relative">
+
+ {isLoadingImage &&
}
+
setIsLoadingImage(false)}
+ onLoad={() => setIsLoadingImage(false)}
+ />
+
+
+ }
+ onClick={(e) => {
+ e.stopPropagation()
+ setMediaId(null)
+ const updatedVariantImages = {
+ ...experiment.parameters?.variant_screenshot_media_ids,
+ }
+ delete updatedVariantImages[variantKey]
+ updateExperimentVariantImages(updatedVariantImages)
+ }}
+ size="small"
+ tooltip="Remove"
+ tooltipPlacement="right"
+ noPadding
+ className="group-hover:flex hidden absolute right-0 top-0"
+ />
+
+
+
+
+ )}
+ setIsModalOpen(false)}
+ title={
+
+ Screenshot
+
+
+ {rolloutPercentage !== undefined && (
+ ({rolloutPercentage}% rollout)
+ )}
+
+ }
+ >
+
+
+
+ )
+}
diff --git a/frontend/src/scenes/experiments/experimentLogic.tsx b/frontend/src/scenes/experiments/experimentLogic.tsx
index bdcd2b51d4f..f93ce8c45ab 100644
--- a/frontend/src/scenes/experiments/experimentLogic.tsx
+++ b/frontend/src/scenes/experiments/experimentLogic.tsx
@@ -169,6 +169,7 @@ export const experimentLogic = kea([
closeShipVariantModal: true,
setCurrentFormStep: (stepIndex: number) => ({ stepIndex }),
moveToNextFormStep: true,
+ updateExperimentVariantImages: (variantPreviewMediaIds: Record) => ({ variantPreviewMediaIds }),
}),
reducers({
experiment: [
@@ -587,15 +588,12 @@ export const experimentLogic = kea([
},
updateExperimentSuccess: async ({ experiment }) => {
actions.updateExperiments(experiment)
-
if (values.changingGoalMetric) {
actions.loadExperimentResults()
}
-
if (values.changingSecondaryMetrics) {
actions.loadSecondaryMetricResults()
}
-
if (values.experiment?.start_date) {
actions.loadExperimentResults()
}
@@ -717,6 +715,22 @@ export const experimentLogic = kea([
lemonToast.error(error)
actions.closeShipVariantModal()
},
+ updateExperimentVariantImages: async ({ variantPreviewMediaIds }) => {
+ try {
+ const updatedParameters = {
+ ...values.experiment.parameters,
+ variant_screenshot_media_ids: variantPreviewMediaIds,
+ }
+ await api.update(`api/projects/${values.currentTeamId}/experiments/${values.experimentId}`, {
+ parameters: updatedParameters,
+ })
+ actions.setExperiment({
+ parameters: updatedParameters,
+ })
+ } catch (error) {
+ lemonToast.error('Failed to update experiment variant images')
+ }
+ },
})),
loaders(({ actions, props, values }) => ({
experiment: {
diff --git a/frontend/src/types.ts b/frontend/src/types.ts
index 233cf88a25c..7357eb4f939 100644
--- a/frontend/src/types.ts
+++ b/frontend/src/types.ts
@@ -3219,6 +3219,7 @@ export interface Experiment {
feature_flag_variants: MultivariateFlagVariant[]
custom_exposure_filter?: FilterType
aggregation_group_type_index?: integer
+ variant_screenshot_media_ids?: Record
}
start_date?: string | null
end_date?: string | null