diff --git a/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx b/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx
index 96d3dd3ff86e4..06c1ba1707b22 100644
--- a/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx
+++ b/frontend/src/scenes/experiments/ExperimentView/ExperimentView.tsx
@@ -12,10 +12,12 @@ import { SavedMetricModal } from '../Metrics/SavedMetricModal'
import { MetricsView } from '../MetricsView/MetricsView'
import {
ExperimentLoadingAnimation,
+ ExploreButton,
LoadingState,
NoResultsEmptyState,
PageHeaderCustom,
ResultsHeader,
+ ResultsQuery,
} from './components'
import { CumulativeExposuresChart } from './CumulativeExposuresChart'
import { DataCollection } from './DataCollection'
@@ -26,17 +28,58 @@ import { Overview } from './Overview'
import { ReleaseConditionsModal, ReleaseConditionsTable } from './ReleaseConditionsTable'
import { Results } from './Results'
import { SecondaryMetricsTable } from './SecondaryMetricsTable'
+import { SummaryTable } from './SummaryTable'
-const ResultsTab = (): JSX.Element => {
- const { experiment, metricResults, featureFlags } = useValues(experimentLogic)
- const result = metricResults?.[0]
- const hasResultsInsight = result && result.insight
+const NewResultsTab = (): JSX.Element => {
+ const { experiment, metricResults } = useValues(experimentLogic)
+ const hasSomeResults = metricResults?.some((result) => result?.insight)
+
+ const hasSinglePrimaryMetric = experiment.metrics.length === 1
return (
-
- {featureFlags[FEATURE_FLAGS.EXPERIMENTS_MULTIPLE_METRICS] ? (
-
- ) : hasResultsInsight ? (
+ <>
+ {!hasSomeResults && (
+ <>
+ {experiment.type === 'web' ? (
+
+ ) : (
+
+ )}
+ >
+ )}
+ {/* Show overview if there's only a single primary metric */}
+ {hasSinglePrimaryMetric && (
+
+
+
+ )}
+
+ {/* Show detailed results if there's only a single primary metric */}
+ {hasSomeResults && hasSinglePrimaryMetric && (
+
+ )}
+
+ >
+ )
+}
+
+const OldResultsTab = (): JSX.Element => {
+ const { experiment, metricResults } = useValues(experimentLogic)
+ const hasSomeResults = metricResults?.some((result) => result?.insight)
+
+ return (
+ <>
+ {hasSomeResults ? (
) : (
<>
@@ -54,15 +97,16 @@ const ResultsTab = (): JSX.Element => {
)}
>
)}
- {featureFlags[FEATURE_FLAGS.EXPERIMENTS_MULTIPLE_METRICS] ? (
-
- ) : (
-
- )}
-
+
+ >
)
}
+const ResultsTab = (): JSX.Element => {
+ const { featureFlags } = useValues(experimentLogic)
+ return <>{featureFlags[FEATURE_FLAGS.EXPERIMENTS_MULTIPLE_METRICS] ?
:
}>
+}
+
const VariantsTab = (): JSX.Element => {
return (
@@ -87,8 +131,8 @@ export function ExperimentView(): JSX.Element {
} = useValues(experimentLogic)
const { setTabKey } = useActions(experimentLogic)
- const result = metricResults?.[0]
- const hasResultsInsight = result && result.insight
+ // Instead, check if any result in the array has an insight
+ const hasSomeResults = metricResults?.some((result) => result?.insight)
return (
<>
@@ -103,8 +147,9 @@ export function ExperimentView(): JSX.Element {
) : (
<>
- {hasResultsInsight && !featureFlags[FEATURE_FLAGS.EXPERIMENTS_MULTIPLE_METRICS] ? (
+ {hasSomeResults && !featureFlags[FEATURE_FLAGS.EXPERIMENTS_MULTIPLE_METRICS] ? (
+
Summary
diff --git a/frontend/src/scenes/experiments/ExperimentView/Overview.tsx b/frontend/src/scenes/experiments/ExperimentView/Overview.tsx
index 351e58e946646..a6765a64f3f9f 100644
--- a/frontend/src/scenes/experiments/ExperimentView/Overview.tsx
+++ b/frontend/src/scenes/experiments/ExperimentView/Overview.tsx
@@ -71,7 +71,6 @@ export function Overview({ metricIndex = 0 }: { metricIndex?: number }): JSX.Ele
return (
-
Summary
diff --git a/frontend/src/scenes/experiments/ExperimentView/Results.tsx b/frontend/src/scenes/experiments/ExperimentView/Results.tsx
index b0b291554ef90..b031ae5bdf594 100644
--- a/frontend/src/scenes/experiments/ExperimentView/Results.tsx
+++ b/frontend/src/scenes/experiments/ExperimentView/Results.tsx
@@ -15,7 +15,7 @@ export function Results(): JSX.Element {
-
+
)
}
diff --git a/frontend/src/scenes/experiments/ExperimentView/components.tsx b/frontend/src/scenes/experiments/ExperimentView/components.tsx
index cd252785b116c..b52df2d7207a8 100644
--- a/frontend/src/scenes/experiments/ExperimentView/components.tsx
+++ b/frontend/src/scenes/experiments/ExperimentView/components.tsx
@@ -20,35 +20,23 @@ import { FEATURE_FLAGS } from 'lib/constants'
import { dayjs } from 'lib/dayjs'
import { IconAreaChart } from 'lib/lemon-ui/icons'
import { More } from 'lib/lemon-ui/LemonButton/More'
-import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { useEffect, useState } from 'react'
import { urls } from 'scenes/urls'
import { groupsModel } from '~/models/groupsModel'
-import { filtersToQueryNode } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode'
-import { queryFromFilters } from '~/queries/nodes/InsightViz/utils'
import { Query } from '~/queries/Query/Query'
import {
- CachedExperimentFunnelsQueryResponse,
- CachedExperimentTrendsQueryResponse,
ExperimentFunnelsQueryResponse,
ExperimentTrendsQueryResponse,
InsightQueryNode,
InsightVizNode,
NodeKind,
} from '~/queries/schema'
-import {
- Experiment,
- Experiment as ExperimentType,
- ExperimentIdType,
- ExperimentResults,
- InsightShortId,
- InsightType,
-} from '~/types'
+import { Experiment, Experiment as ExperimentType, ExperimentIdType, InsightShortId, InsightType } from '~/types'
import { experimentLogic } from '../experimentLogic'
import { getExperimentStatus, getExperimentStatusColor } from '../experimentsLogic'
-import { getExperimentInsightColour, transformResultFilters } from '../utils'
+import { getExperimentInsightColour } from '../utils'
export function VariantTag({
experimentId,
@@ -128,79 +116,39 @@ export function ResultsTag({ metricIndex = 0 }: { metricIndex?: number }): JSX.E
}
export function ResultsQuery({
- targetResults,
+ result,
showTable,
}: {
- targetResults: ExperimentResults['result'] | ExperimentTrendsQueryResponse | ExperimentFunnelsQueryResponse | null
+ result: ExperimentTrendsQueryResponse | ExperimentFunnelsQueryResponse | null
showTable: boolean
}): JSX.Element {
- const { featureFlags } = useValues(featureFlagLogic)
- if (featureFlags[FEATURE_FLAGS.EXPERIMENTS_HOGQL]) {
- const newQueryResults = targetResults as unknown as
- | CachedExperimentTrendsQueryResponse
- | CachedExperimentFunnelsQueryResponse
-
- const query =
- newQueryResults.kind === NodeKind.ExperimentTrendsQuery
- ? newQueryResults.count_query
- : newQueryResults.funnels_query
- const fakeInsightId = Math.random().toString(36).substring(2, 15)
-
- return (
-
- )
- }
-
- const oldQueryResults = targetResults as ExperimentResults['result']
-
- if (!oldQueryResults?.filters) {
+ if (!result) {
return <>>
}
+ const query = result.kind === NodeKind.ExperimentTrendsQuery ? result.count_query : result.funnels_query
+ const fakeInsightId = Math.random().toString(36).substring(2, 15)
+
return (
>
}
@@ -234,7 +179,7 @@ export function ExploreButton({
return (
}
to={urls.insightNew(undefined, undefined, query)}
@@ -260,7 +205,7 @@ export function ResultsHeader(): JSX.Element {
-
{result && }
+
{result && }
)
@@ -580,12 +525,17 @@ export function PageHeaderCustom(): JSX.Element {
}
export function ShipVariantModal({ experimentId }: { experimentId: Experiment['id'] }): JSX.Element {
- const { experiment, sortedWinProbabilities, isShipVariantModalOpen } = useValues(experimentLogic({ experimentId }))
+ const { experiment, isShipVariantModalOpen } = useValues(experimentLogic({ experimentId }))
const { closeShipVariantModal, shipVariant } = useActions(experimentLogic({ experimentId }))
const { aggregationLabel } = useValues(groupsModel)
const [selectedVariantKey, setSelectedVariantKey] = useState
()
- useEffect(() => setSelectedVariantKey(sortedWinProbabilities(0)[0]?.key), [sortedWinProbabilities(0)])
+ useEffect(() => {
+ if (experiment.parameters?.feature_flag_variants?.length > 1) {
+ // First test variant selected by default
+ setSelectedVariantKey(experiment.parameters.feature_flag_variants[1].key)
+ }
+ }, [experiment])
const aggregationTargetName =
experiment.filters.aggregation_group_type_index != null
@@ -625,20 +575,19 @@ export function ShipVariantModal({ experimentId }: { experimentId: Experiment['i
className="w-full"
data-attr="metrics-selector"
value={selectedVariantKey}
- onChange={(variantKey) => setSelectedVariantKey(variantKey)}
- options={sortedWinProbabilities(0).map(({ key }) => ({
- value: key,
- label: (
-
-
- {key === sortedWinProbabilities(0)[0]?.key && (
-
- Winning
-
- )}
-
- ),
- }))}
+ onChange={(variantKey) => {
+ setSelectedVariantKey(variantKey)
+ }}
+ options={
+ experiment.parameters?.feature_flag_variants?.map(({ key }) => ({
+ value: key,
+ label: (
+
+
+
+ ),
+ })) || []
+ }
/>
diff --git a/frontend/src/scenes/experiments/Metrics/SecondaryMetricChartModal.tsx b/frontend/src/scenes/experiments/Metrics/SecondaryMetricChartModal.tsx
index ec540aa43c056..184137bb59be6 100644
--- a/frontend/src/scenes/experiments/Metrics/SecondaryMetricChartModal.tsx
+++ b/frontend/src/scenes/experiments/Metrics/SecondaryMetricChartModal.tsx
@@ -32,7 +32,7 @@ export function SecondaryMetricChartModal({
}
>
-
+
)
}
diff --git a/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx b/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx
index 820b7b38acfd3..88d24827fa63a 100644
--- a/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx
+++ b/frontend/src/scenes/experiments/MetricsView/DeltaChart.tsx
@@ -1,4 +1,12 @@
-import { IconActivity, IconGraph, IconMinus, IconPencil, IconTrending } from '@posthog/icons'
+import {
+ IconActivity,
+ IconArrowRight,
+ IconFunnels,
+ IconGraph,
+ IconMinus,
+ IconPencil,
+ IconTrending,
+} from '@posthog/icons'
import { LemonBanner, LemonButton, LemonModal, LemonTag, LemonTagType, Tooltip } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { LemonProgress } from 'lib/lemon-ui/LemonProgress'
@@ -35,6 +43,34 @@ function formatTickValue(value: number): string {
return `${(value * 100).toFixed(decimals)}%`
}
+const getMetricTitle = (metric: any, metricType: InsightType): JSX.Element => {
+ if (metric.name) {
+ return
{metric.name}
+ }
+
+ if (metricType === InsightType.TRENDS && metric.count_query?.series?.[0]?.name) {
+ return
{metric.count_query.series[0].name}
+ }
+
+ if (metricType === InsightType.FUNNELS && metric.funnels_query?.series) {
+ const series = metric.funnels_query.series
+ if (series.length > 0) {
+ const firstStep = series[0]?.name
+ const lastStep = series[series.length - 1]?.name
+
+ return (
+
+
+ {firstStep}
+
+ {lastStep}
+
+ )
+ }
+ }
+
+ return
Untitled metric
+}
export function DeltaChart({
isSecondary,
@@ -183,9 +219,9 @@ export function DeltaChart({
-
- {metricIndex + 1}.{' '}
- {metric.name ||
Untitled metric}
+
+ {metricIndex + 1}.
+ {getMetricTitle(metric, metricType)}
-
- }
- onClick={() => setIsModalOpen(true)}
- >
- Detailed results
-
-
+ {experiment.metrics.length > 1 && (
+
+ }
+ onClick={() => setIsModalOpen(true)}
+ >
+ Detailed results
+
+
+ )}
)}
@@ -274,34 +312,40 @@ export function DeltaChart({
{isFirstMetric &&
}
{/* eslint-disable-next-line react/forbid-dom-props */}
- {variants.map((variant) => (
-
+ {result &&
+ variants.map((variant) => (
-
-
- ))}
+ ))}
{/* SVGs container */}
@@ -537,7 +581,7 @@ export function DeltaChart({
) : (
-
+
@@ -792,7 +836,7 @@ export function DeltaChart({
-
+
)
diff --git a/frontend/src/scenes/experiments/MetricsView/MetricsView.tsx b/frontend/src/scenes/experiments/MetricsView/MetricsView.tsx
index 9a362d9961974..2b5735778e1c3 100644
--- a/frontend/src/scenes/experiments/MetricsView/MetricsView.tsx
+++ b/frontend/src/scenes/experiments/MetricsView/MetricsView.tsx
@@ -1,5 +1,5 @@
import { IconInfo, IconPlus } from '@posthog/icons'
-import { LemonButton, Tooltip } from '@posthog/lemon-ui'
+import { LemonButton, LemonDivider, Tooltip } from '@posthog/lemon-ui'
import { useActions, useValues } from 'kea'
import { IconAreaChart } from 'lib/lemon-ui/icons'
@@ -101,6 +101,7 @@ export function MetricsView({ isSecondary }: { isSecondary?: boolean }): JSX.Ele
const variants = experiment.parameters.feature_flag_variants
const results = isSecondary ? secondaryMetricResults : metricResults
const errors = isSecondary ? secondaryMetricsResultErrors : primaryMetricsResultErrors
+ const hasSomeResults = results?.some((result) => result?.insight)
let metrics = isSecondary ? experiment.metrics_secondary : experiment.metrics
const savedMetrics = experiment.saved_metrics
@@ -155,6 +156,52 @@ export function MetricsView({ isSecondary }: { isSecondary?: boolean }): JSX.Ele
)}
+ {hasSomeResults && !isSecondary && (
+ <>
+
+
+
+ Each bar shows how a variant is performing compared to the control (the
+ gray bar) for this metric, using a{' '}
+ 95% credible interval. That means there's a 95% chance
+ the true difference for that variant falls within this range. The
+ vertical "0%" line is your baseline:
+
+
+ -
+ To the right (green): The metric is higher (an
+ improvement).
+
+ -
+ To the left (red): The metric is lower (a
+ decrease).
+
+
+
+ The width of the bar represents uncertainty. A{' '}
+ narrower bar means we're more confident in that result,
+ while a wider bar means it could shift either way.
+
+
+ The control (baseline) is always shown in gray. Other bars will be green
+ or red—or even a mix—depending on whether the change is positive or
+ negative.
+
+
+
+ }
+ >
+
+
+ >
+ )}