Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getFormattedDate } from 'scenes/insights/InsightTooltip/insightTooltipUtils'
import { formatCompareLabelWithDate, getFormattedDate } from 'scenes/insights/InsightTooltip/insightTooltipUtils'

import { IntervalType } from '~/types'

Expand Down Expand Up @@ -157,3 +157,13 @@ describe('getFormattedDate', () => {
})
})
})

describe('formatCompareLabelWithDate', () => {
it('appends the actual period date when present', () => {
expect(formatCompareLabelWithDate('previous', '3 Jun 2024')).toBe('Previous (3 Jun 2024)')
})

it('keeps the existing label when no date is available', () => {
expect(formatCompareLabelWithDate('previous')).toBe('Previous')
})
})
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,10 @@ export const INTERVAL_UNIT_TO_DAYJS_FORMAT: Record<IntervalType, string> = {
month: 'MMMM YYYY',
}

export function formatCompareLabelWithDate(label: string, dateLabel?: string): string {
return `${capitalizeFirstLetter(label)}${dateLabel ? ` (${dateLabel})` : ''}`
}

const INTERVAL_UNIT_TO_DAYJS_FORMAT_SHORT: Record<IntervalType, string> = {
second: 'D MMM HH:mm:ss',
minute: 'D MMM HH:mm',
Expand Down Expand Up @@ -207,7 +211,7 @@ function getPillValues(
if (s.compare_label) {
const formattedLabel = formatCompareLabel
? formatCompareLabel(String(s.compare_label), s.date_label)
: capitalizeFirstLetter(String(s.compare_label))
: formatCompareLabelWithDate(String(s.compare_label), s.date_label)
pillValues.push(formattedLabel)
}
return pillValues
Expand Down
46 changes: 28 additions & 18 deletions frontend/src/scenes/insights/views/LineGraph/LineGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ export function LineGraph_({

const { aggregationLabel } = useValues(groupsModel)
const { isDarkModeOn } = useValues(themeLogic)
const { baseCurrency } = useValues(teamLogic)
const { baseCurrency, weekStartDay } = useValues(teamLogic)

const { insightProps, insight } = useValues(insightLogic)
const { timezone, isTrends, isStickiness, isFunnels, breakdownFilter, interval, insightData } = useValues(
Expand Down Expand Up @@ -850,25 +850,35 @@ export function LineGraph_({
const referenceDataPoint = tooltip.dataPoints[0]
const dataset = processedDatasets[referenceDataPoint.datasetIndex]
const date = dataset?.days?.[referenceDataPoint.dataIndex]
const seriesData = createTooltipData(tooltip.dataPoints, (dp) => {
// For stacked bar charts when shift is pressed, show only the hovered bar
if (isHighlightBarMode) {
return dp.datasetIndex === referenceDataPoint.datasetIndex
}
const seriesData = createTooltipData(
tooltip.dataPoints,
(dp) => {
// For stacked bar charts when shift is pressed, show only the hovered bar
if (isHighlightBarMode) {
return dp.datasetIndex === referenceDataPoint.datasetIndex
}

if (tooltipConfig?.filter) {
return tooltipConfig.filter(dp)
}
if (tooltipConfig?.filter) {
return tooltipConfig.filter(dp)
}

const hasDotted =
hasAnyDottedDataset &&
dp.dataIndex - processedDatasets?.[dp.datasetIndex]?.data?.length >=
incompletenessOffsetFromEnd
return (
dp.datasetIndex >= (hasDotted ? _datasets.length : 0) &&
dp.datasetIndex < (hasDotted ? _datasets.length * 2 : _datasets.length)
)
})
const hasDotted =
hasAnyDottedDataset &&
dp.dataIndex - processedDatasets?.[dp.datasetIndex]?.data?.length >=
incompletenessOffsetFromEnd
return (
dp.datasetIndex >= (hasDotted ? _datasets.length : 0) &&
dp.datasetIndex < (hasDotted ? _datasets.length * 2 : _datasets.length)
)
},
{
interval,
dateRange: insightData?.resolved_date_range,
compareDateRange: insightData?.resolved_compare_date_range,
timezone,
weekStartDay,
}
)
const referenceSeriesDatum = seriesData.find(
(datum) =>
datum.datasetIndex === referenceDataPoint.datasetIndex &&
Expand Down
128 changes: 128 additions & 0 deletions frontend/src/scenes/insights/views/LineGraph/tooltip-data.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { TooltipItem } from 'lib/Chart'

import { createTooltipData } from './tooltip-data'

function tooltipItem(dataset: Record<string, any>, dataIndex = 0, datasetIndex = 0): TooltipItem<any> {
return {
dataIndex,
datasetIndex,
dataset,
} as TooltipItem<any>
}

describe('createTooltipData', () => {
it.each([
['day interval', '2024-06-03', 'day', 'UTC', '3 Jun 2024'],
['month interval', '2024-02-01', 'month', 'UTC', 'February 2024'],
['timezone boundary', '2024-04-28T23:30:00Z', 'day', 'Asia/Tokyo', '29 Apr 2024'],
])('formats compare date labels for %s', (_, day, interval, timezone, expectedDateLabel) => {
const data = createTooltipData(
[
tooltipItem({
data: [10],
days: [day],
label: '$pageview',
compare: true,
compare_label: 'previous',
}),
],
undefined,
{ interval: interval as any, timezone }
)

expect(data[0].date_label).toBe(expectedDateLabel)
})

it('formats week interval labels as actual date ranges', () => {
const data = createTooltipData(
[
tooltipItem({
data: [10],
days: ['2024-04-28'],
label: '$pageview',
compare: true,
compare_label: 'previous',
}),
],
undefined,
{ interval: 'week', timezone: 'UTC' }
)

expect(data[0].date_label).toBe('28 Apr - 4 May 2024')
})

it('respects explicit date boundaries for weekly labels', () => {
const data = createTooltipData(
[
tooltipItem({
data: [10],
days: ['2024-04-30'],
label: '$pageview',
compare: true,
compare_label: 'current',
}),
],
undefined,
{
interval: 'week',
timezone: 'UTC',
dateRange: { date_from: '2024-04-30', date_to: '2024-05-02' },
}
)

expect(data[0].date_label).toBe('30 Apr - 2 May 2024')
})

it('uses the compare date range for previous weekly labels', () => {
const data = createTooltipData(
[
tooltipItem({
data: [10],
days: ['2024-06-03'],
label: '$pageview',
compare: true,
compare_label: 'previous',
}),
],
undefined,
{
interval: 'week',
timezone: 'UTC',
dateRange: { date_from: '2024-06-06', date_to: '2024-06-12' },
compareDateRange: { date_from: '2024-05-30', date_to: '2024-06-05' },
}
)

expect(data[0].date_label).toBe('2-5 Jun 2024')
})

it('falls back to backend labels when a dataset has no day values', () => {
const data = createTooltipData([
tooltipItem({
data: [10],
labels: ['3-Jun-2024'],
label: '$pageview',
compare: true,
compare_label: 'previous',
}),
])

expect(data[0].date_label).toBe('3-Jun-2024')
})

it('falls back to backend labels when formatting options are omitted', () => {
const data = createTooltipData([
tooltipItem(
{
data: [10, 20],
days: ['2024-06-03', '2024-06-04'],
labels: ['Pageview', 'Signup'],
compareLabels: ['previous', 'current'],
},
1
),
])

expect(data[0].date_label).toBe('Signup')
})
})
33 changes: 28 additions & 5 deletions frontend/src/scenes/insights/views/LineGraph/tooltip-data.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,41 @@
import { TooltipItem } from 'lib/Chart'
import { SeriesDatum } from 'scenes/insights/InsightTooltip/insightTooltipUtils'
import { getFormattedDate, SeriesDatum } from 'scenes/insights/InsightTooltip/insightTooltipUtils'

import { GraphDataset } from '~/types'
import { DateRange } from '~/queries/schema/schema-general'
import { GraphDataset, IntervalType } from '~/types'

export interface TooltipDataOptions {
interval?: IntervalType | null
dateRange?: DateRange | null
compareDateRange?: DateRange | null
timezone?: string
weekStartDay?: number
}

export function createTooltipData(
tooltipDataPoints: TooltipItem<any>[],
filterFn?: (s: SeriesDatum) => boolean
filterFn?: (s: SeriesDatum) => boolean,
options?: TooltipDataOptions
): SeriesDatum[] {
if (!tooltipDataPoints) {
return []
}
let data = tooltipDataPoints
.map((dp, idx) => {
const pointDataset = (dp?.dataset ?? {}) as GraphDataset
const date = pointDataset?.days?.[dp.dataIndex]
const compareLabel = pointDataset?.compare_label ?? pointDataset?.compareLabels?.[dp.dataIndex] ?? undefined
const dateRange =
compareLabel === 'previous' ? (options?.compareDateRange ?? options?.dateRange) : options?.dateRange
const dateLabel =
typeof date === 'string' && options
? getFormattedDate(date, {
interval: options?.interval,
dateRange,
timezone: options?.timezone,
weekStartDay: options?.weekStartDay,
})
: (pointDataset?.labels?.[dp.dataIndex] ?? undefined)
return {
id: idx,
dataIndex: dp.dataIndex,
Expand All @@ -24,10 +47,10 @@ export function createTooltipData(
pointDataset?.breakdownLabels?.[dp.dataIndex] ??
pointDataset?.breakdownValues?.[dp.dataIndex] ??
undefined,
compare_label: pointDataset?.compare_label ?? pointDataset?.compareLabels?.[dp.dataIndex] ?? undefined,
compare_label: compareLabel,
action: pointDataset?.action ?? pointDataset?.actions?.[dp.dataIndex] ?? undefined,
label: pointDataset?.label ?? pointDataset.labels?.[dp.dataIndex] ?? undefined,
date_label: pointDataset?.labels?.[dp.dataIndex] ?? undefined,
date_label: dateLabel,
order: pointDataset?.order ?? 0,
color: Array.isArray(pointDataset.borderColor)
? pointDataset.borderColor?.[dp.dataIndex]
Expand Down