diff --git a/static/app/views/explore/components/attributeBreakdowns/content.tsx b/static/app/views/explore/components/attributeBreakdowns/content.tsx index 95ae2e1ba60bf4..ec83853a344588 100644 --- a/static/app/views/explore/components/attributeBreakdowns/content.tsx +++ b/static/app/views/explore/components/attributeBreakdowns/content.tsx @@ -1,6 +1,7 @@ import {Fragment, useEffect, useMemo, useState} from 'react'; import {useTheme} from '@emotion/react'; import styled from '@emotion/styled'; +import moment from 'moment-timezone'; import emptyTraceImg from 'sentry-images/spot/performance-empty-trace.svg'; @@ -17,11 +18,13 @@ import {IconChevron} from 'sentry/icons/iconChevron'; import {IconMegaphone} from 'sentry/icons/iconMegaphone'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; +import {getUserTimezone} from 'sentry/utils/dates'; import {useDebouncedValue} from 'sentry/utils/useDebouncedValue'; import {useFeedbackForm} from 'sentry/utils/useFeedbackForm'; import type {ChartInfo} from 'sentry/views/explore/components/chart/types'; import useAttributeBreakdowns from 'sentry/views/explore/hooks/useAttributeBreakdowns'; import type {BoxSelectOptions} from 'sentry/views/explore/hooks/useChartBoxSelect'; +import {prettifyAggregation} from 'sentry/views/explore/utils'; import {Chart} from './chart'; import {useChartSelection} from './chartSelectionContext'; @@ -29,6 +32,7 @@ import {SortingToggle, type SortingMethod} from './sortingToggle'; const CHARTS_COLUMN_COUNT = 3; const CHARTS_PER_PAGE = CHARTS_COLUMN_COUNT * 4; +const PERCENTILE_FUNCTION_PREFIXES = ['p50', 'p75', 'p90', 'p95', 'p99', 'avg']; function FeedbackButton() { const openForm = useFeedbackForm(); @@ -70,7 +74,7 @@ function EmptyState() { {t( - "Drag to select an area on the chart and click 'Find Attribute Breakdowns' to analyze differences between selected and unselected (baseline) data. Attributes that differ most in frequency appear first, making it easier to identify key differences:" + "Drag to select an area on the chart and click 'Compare Attribute Breakdowns' to analyze differences between selected and unselected (baseline) data. Attributes that differ most in frequency appear first, making it easier to identify key differences:" )} @@ -133,6 +137,44 @@ function ContentImpl({ setPage(0); }, [filteredRankedAttributes]); + const selectionHint = useMemo(() => { + if (!boxSelectOptions.xRange) { + return null; + } + + const [x1, x2] = boxSelectOptions.xRange; + + let startTimestamp = Math.floor(x1 / 60_000) * 60_000; + const endTimestamp = Math.ceil(x2 / 60_000) * 60_000; + startTimestamp = Math.min(startTimestamp, endTimestamp - 60_000); + + const userTimezone = getUserTimezone() || moment.tz.guess(); + const startDate = moment + .tz(startTimestamp, userTimezone) + .format('MMM D YYYY h:mm A z'); + const endDate = moment.tz(endTimestamp, userTimezone).format('MMM D YYYY h:mm A z'); + + // Check if yAxis is a percentile function (only these functions should include "and is greater than or equal to") + const yAxisLower = chartInfo.yAxis.toLowerCase(); + const isPercentileFunction = PERCENTILE_FUNCTION_PREFIXES.some(prefix => + yAxisLower.startsWith(prefix) + ); + + const formattedFunction = prettifyAggregation(chartInfo.yAxis) ?? chartInfo.yAxis; + + return { + selection: isPercentileFunction + ? t( + `Selection is data between %s - %s and is greater than or equal to %s`, + startDate, + endDate, + formattedFunction + ) + : t(`Selection is data between %s - %s`, startDate, endDate), + baseline: t('Baseline is all other spans from your query'), + }; + }, [boxSelectOptions.xRange, chartInfo.yAxis]); + return ( {isLoading ? ( @@ -152,6 +194,14 @@ function ContentImpl({ /> + {selectionHint && ( + + + {selectionHint.selection} + + {selectionHint.baseline} + + )} {filteredRankedAttributes.length > 0 ? ( @@ -269,3 +319,27 @@ const PaginationContainer = styled('div')` justify-content: end; align-items: center; `; + +const SelectionHintContainer = styled('div')` + display: flex; + flex-direction: column; + gap: ${space(0.5)}; + margin-bottom: ${space(1)}; +`; + +const SelectionHint = styled(Text)<{color?: string}>` + display: flex; + align-items: center; + color: ${p => p.theme.subText}; + font-size: ${p => p.theme.fontSize.sm}; + + &::before { + content: ''; + width: 8px; + height: 8px; + border-radius: 50%; + background-color: ${p => p.color || p.theme.gray400}; + margin-right: ${space(0.5)}; + flex-shrink: 0; + } +`; diff --git a/static/app/views/explore/components/attributeBreakdowns/floatingTrigger.tsx b/static/app/views/explore/components/attributeBreakdowns/floatingTrigger.tsx index 3703774829ff29..5ca60441ab1942 100644 --- a/static/app/views/explore/components/attributeBreakdowns/floatingTrigger.tsx +++ b/static/app/views/explore/components/attributeBreakdowns/floatingTrigger.tsx @@ -81,7 +81,7 @@ export function FloatingTrigger({ {t('Zoom in')} - {t('Find Attribute Breakdowns')} + {t('Compare Attribute Breakdowns')} ,