Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(funnels): Highlight significant deviations in new funnel viz #9536

Merged
merged 5 commits into from
Apr 27, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion frontend/src/lib/components/LemonTable/LemonTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export interface LemonTableProps<T extends Record<string, any>> {
/** Which column to use for the row key, as an alternative to the default row index mechanism. */
rowKey?: keyof T | ((record: T) => string | number)
/** Class to append to each row. */
rowClassName?: string | ((record: T) => string)
rowClassName?: string | ((record: T) => string | null)
/** Color to mark each row with. */
rowRibbonColor?: string | ((record: T) => string | null)
/** Status of each row. Defaults no status. */
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/components/LemonTable/TableRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface TableRowProps<T extends Record<string, any>> {
record: T
recordIndex: number
rowKeyDetermined: string | number
rowClassNameDetermined: string | undefined
rowClassNameDetermined: string | null | undefined
rowRibbonColorDetermined: string | null | undefined
rowStatusDetermined: 'success' | 'warning' | 'danger' | 'highlighted' | undefined
columnGroups: LemonTableColumnGroup<T>[]
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/components/icons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -437,9 +437,9 @@ export function IconGroupedEvents(props: React.SVGProps<SVGSVGElement>): JSX.Ele
}

/** Material Design Assistant Photo icon. */
export function IconFlag(): JSX.Element {
export function IconFlag(props: React.SVGProps<SVGSVGElement>): JSX.Element {
return (
<svg fill="none" width="1em" height="1em" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<svg fill="none" width="1em" height="1em" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" {...props}>
<path
d="m12.36 6 .08.39.32 1.61h5.24v6h-3.36l-.08-.39-.32-1.61h-7.24v-6zm1.64-2h-9v17h2v-7h5.6l.4 2h7v-10h-5.6z"
fill="currentColor"
Expand Down
11 changes: 6 additions & 5 deletions frontend/src/scenes/funnels/funnelLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ import { groupsModel } from '~/models/groupsModel'
import { dayjs } from 'lib/dayjs'
import { lemonToast } from 'lib/components/lemonToast'

/* Chosen via heuristics by eyeballing some values
* Assuming a normal distribution, then 90% of values are within 1.5 standard deviations of the mean
* which gives a ballpark of 1 highlighting every 10 breakdown values
*/
const DEVIATION_SIGNIFICANCE_MULTIPLIER = 1.5
// Chosen via heuristics by eyeballing some values
// Assuming a normal distribution, then 90% of values are within 1.5 standard deviations of the mean
// which gives a ballpark of 1 highlighting every 10 breakdown values

// List of events that should be excluded, if we don't have an explicit list of
// excluded properties. Copied from
Expand Down Expand Up @@ -825,8 +826,8 @@ export const funnelLogic = kea<funnelLogicType<openPersonsModelProps>>({
(stepsInBreakdown[stepsInBreakdown.length - 1]?.count ?? 0) /
(stepsInBreakdown[0]?.count ?? 1),
},
significant: stepsInBreakdown.some((step) =>
step.significant ? Object.values(step.significant).some((val) => val) : false
significant: stepsInBreakdown.some(
(step) => step.significant?.total || step.significant?.fromBasisStep
),
})
})
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/scenes/insights/Insight.scss
Original file line number Diff line number Diff line change
Expand Up @@ -286,3 +286,12 @@ $funnel_canvas_background: #fff;
}
}
}

.significance-highlight {
display: inline-flex;
background: var(--primary);
color: var(--bg-light);
.LemonRow__icon {
color: var(--bg-light);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ import { LemonTable, LemonTableColumn, LemonTableColumnGroup } from 'lib/compone
import { BreakdownKeyType, FlattenedFunnelStepByBreakdown } from '~/types'
import { EntityFilterInfo } from 'lib/components/EntityFilterInfo'
import { formatDisplayPercentage, getSeriesColor, getVisibilityIndex } from 'scenes/funnels/funnelUtils'
import { getActionFilterFromFunnelStep } from './funnelStepTableUtils'
import { getActionFilterFromFunnelStep, getSignificanceFromBreakdownStep } from './funnelStepTableUtils'
import { formatBreakdownLabel } from 'scenes/insights/InsightsTable/InsightsTable'
import { cohortsModel } from '~/models/cohortsModel'
import { LemonCheckbox } from 'lib/components/LemonCheckbox'
import { Lettermark, LettermarkColor } from 'lib/components/Lettermark/Lettermark'
import { LemonRow } from 'lib/components/LemonRow'
import { humanFriendlyDuration } from 'lib/utils'
import { ValueInspectorButton } from 'scenes/funnels/FunnelBarGraph'
import { IconFlag } from 'lib/components/icons'

export function FunnelStepsTable(): JSX.Element | null {
const { insightProps } = useValues(insightLogic)
Expand Down Expand Up @@ -92,8 +93,23 @@ export function FunnelStepsTable(): JSX.Element | null {
},
{
title: 'Total conversion',
render: (_: void, breakdown: FlattenedFunnelStepByBreakdown) =>
formatDisplayPercentage(breakdown?.conversionRates?.total ?? 0, true),
render: function RenderTotalConversion(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious about when and where does 'Total conversion' show up? Not sure I ever see this. Otherwise LGTM!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect you might have not created and switched on the lemon-funnel-viz flag?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

D'oh, ofcourse. Gahh, let me go again :P

_: void,
breakdown: FlattenedFunnelStepByBreakdown
): JSX.Element | string {
return getSignificanceFromBreakdownStep(breakdown, 0)?.total ? (
<LemonRow
className="significance-highlight"
tooltip="Significantly different from other breakdown values"
icon={<IconFlag />}
compact
>
{formatDisplayPercentage(breakdown?.conversionRates?.total ?? 0, true)}
</LemonRow>
) : (
formatDisplayPercentage(breakdown?.conversionRates?.total ?? 0, true)
)
},
align: 'right',
},
],
Expand Down Expand Up @@ -132,8 +148,29 @@ export function FunnelStepsTable(): JSX.Element | null {
},
{
title: 'Rate',
render: (_: void, breakdown: FlattenedFunnelStepByBreakdown) =>
formatDisplayPercentage(breakdown.steps?.[stepIndex]?.conversionRates.fromPrevious ?? 0, true),
render: function RenderRate(
_: void,
breakdown: FlattenedFunnelStepByBreakdown
): JSX.Element | string {
return getSignificanceFromBreakdownStep(breakdown, step.order)?.fromBasisStep ? (
<LemonRow
className="significance-highlight"
tooltip="Significantly different from other breakdown values"
icon={<IconFlag />}
compact
>
{formatDisplayPercentage(
breakdown.steps?.[step.order]?.conversionRates.fromBasisStep ?? 0,
true
)}
</LemonRow>
) : (
formatDisplayPercentage(
breakdown.steps?.[step.order]?.conversionRates.fromBasisStep ?? 0,
true
)
)
},
align: 'right',
},
...(stepIndex === 0
Expand Down Expand Up @@ -165,7 +202,7 @@ export function FunnelStepsTable(): JSX.Element | null {
title: 'Rate',
render: (_: void, breakdown: FlattenedFunnelStepByBreakdown) =>
formatDisplayPercentage(
1 - (breakdown.steps?.[stepIndex]?.conversionRates.fromPrevious ?? 0),
1 - (breakdown.steps?.[stepIndex]?.conversionRates.fromBasisStep ?? 0),
true
),
align: 'right',
Expand All @@ -189,6 +226,8 @@ export function FunnelStepsTable(): JSX.Element | null {
dataSource={flattenedBreakdowns}
columns={columnsGrouped}
loading={insightLoading}
rowKey="breakdownIndex"
rowStatus={(record) => (record.significant ? 'highlighted' : undefined)}
rowRibbonColor={(series) =>
getSeriesColor(
series?.breakdownIndex,
Expand All @@ -197,7 +236,6 @@ export function FunnelStepsTable(): JSX.Element | null {
flattenedBreakdowns.length
)
}
rowKey="breakdownIndex"
/>
)
}