Skip to content

Commit 9e66e4d

Browse files
nsdeschenesclaude
andauthored
feat(tracemetrics): Refresh the metrics refresh (#111920)
This PR refreshes the refresh of the trace metrics page, changes include (but not limited to): - Moving back to side-by-side orientation - Moving filter bar into the individual metric panels - Changing title from `Metrics` -> `Application Metrics` - Changing visibility toggle to a chevron - Moved/added "Add Metric" and "Save as" buttons Ticket: LOGS-647 --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 7d3b367 commit 9e66e4d

File tree

11 files changed

+146
-378
lines changed

11 files changed

+146
-378
lines changed

static/app/views/explore/metrics/content.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ import {useOnboardingProject} from 'sentry/views/insights/common/queries/useOnbo
2828
import {TopBar} from 'sentry/views/navigation/topBar';
2929
import {useHasPageFrameFeature} from 'sentry/views/navigation/useHasPageFrameFeature';
3030

31+
const METRICS_TITLE = t('Application Metrics');
32+
3133
export default function MetricsContent() {
3234
const organization = useOrganization();
3335
const onboardingProject = useOnboardingProject({property: 'hasTraceMetrics'});
@@ -36,7 +38,7 @@ export default function MetricsContent() {
3638
});
3739
const datePageFilterProps = useDatePageFilterProps(maxPickableDays);
3840
return (
39-
<SentryDocumentTitle title={t('Metrics')} orgSlug={organization?.slug}>
41+
<SentryDocumentTitle title={METRICS_TITLE} orgSlug={organization?.slug}>
4042
<PageFiltersContainer
4143
maxPickableDays={datePageFilterProps.maxPickableDays}
4244
defaultSelection={
@@ -95,15 +97,15 @@ function MetricsHeader() {
9597
<Layout.HeaderContent unified>
9698
{hasSavedQueryTitle ? (
9799
<SentryDocumentTitle
98-
title={`${savedQuery.name}${t('Metrics')}`}
100+
title={`${savedQuery.name}${METRICS_TITLE}`}
99101
orgSlug={organization?.slug}
100102
/>
101103
) : null}
102104
{title && defined(pageId) ? (
103105
<ExploreBreadcrumb traceItemDataset={TraceItemDataset.TRACEMETRICS} />
104106
) : null}
105107
<Layout.Title>
106-
{title ? title : t('Metrics')}
108+
{title ? title : METRICS_TITLE}
107109
<FeatureBadge type="beta" />
108110
</Layout.Title>
109111
</Layout.HeaderContent>

static/app/views/explore/metrics/metricGraph/index.tsx

Lines changed: 1 addition & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import {Fragment, useMemo} from 'react';
2-
import styled from '@emotion/styled';
32

43
import {CompactSelect} from '@sentry/scraps/compactSelect';
5-
import {Flex} from '@sentry/scraps/layout';
64
import {ExternalLink} from '@sentry/scraps/link';
75
import {OverlayTrigger} from '@sentry/scraps/overlayTrigger';
8-
import {Text} from '@sentry/scraps/text';
96

107
import {IconClock, IconGraph} from 'sentry/icons';
118
import {t, tct} from 'sentry/locale';
@@ -37,7 +34,6 @@ import {
3734
useQueryParamsTopEventsLimit,
3835
} from 'sentry/views/explore/queryParams/context';
3936
import {EXPLORE_CHART_TYPE_OPTIONS} from 'sentry/views/explore/spans/charts';
40-
import {getVisualizeLabel} from 'sentry/views/explore/toolbar/toolbarVisualize';
4137
import {useRawCounts} from 'sentry/views/explore/useRawCounts';
4238
import {
4339
combineConfidenceForSeries,
@@ -61,13 +57,11 @@ interface MetricsGraphProps {
6157
additionalActions?: React.ReactNode;
6258
infoContentHidden?: boolean;
6359
isMetricOptionsEmpty?: boolean;
64-
queryIndex?: number;
6560
}
6661

6762
export function MetricsGraph({
6863
timeseriesResult,
6964
orientation,
70-
queryIndex = 0,
7165
additionalActions,
7266
infoContentHidden,
7367
isMetricOptionsEmpty,
@@ -97,7 +91,6 @@ export function MetricsGraph({
9791
additionalActions={additionalActions}
9892
infoContentHidden={infoContentHidden}
9993
isMetricOptionsEmpty={isMetricOptionsEmpty}
100-
queryIndex={queryIndex}
10194
/>
10295
);
10396
}
@@ -117,7 +110,6 @@ function Graph({
117110
infoContentHidden,
118111
additionalActions,
119112
isMetricOptionsEmpty,
120-
queryIndex = 0,
121113
}: GraphProps) {
122114
const organization = useOrganization();
123115
const aggregate = visualize.yAxis;
@@ -189,26 +181,7 @@ function Graph({
189181
return metricLabel ?? prettifyAggregation(aggregate) ?? aggregate;
190182
}, [aggregate, metricLabel, metricName, visualizes.length]);
191183

192-
const Title = canUseMetricsUIRefresh(organization) ? (
193-
<Flex align="center" gap="xs">
194-
<VisualizeLabel
195-
justify="center"
196-
align="center"
197-
radius="md"
198-
paddingLeft="sm"
199-
paddingRight="sm"
200-
paddingTop="xs"
201-
paddingBottom="xs"
202-
>
203-
<Text bold variant="accent">
204-
{getVisualizeLabel(queryIndex)}
205-
</Text>
206-
</VisualizeLabel>
207-
<Widget.WidgetTitle title={chartTitle} />
208-
</Flex>
209-
) : (
210-
<Widget.WidgetTitle title={chartTitle} />
211-
);
184+
const Title = <Widget.WidgetTitle title={chartTitle} />;
212185

213186
const chartIcon =
214187
visualize.chartType === ChartType.LINE
@@ -315,7 +288,3 @@ function Graph({
315288
</WidgetWrapper>
316289
);
317290
}
318-
319-
const VisualizeLabel = styled(Flex)`
320-
background-color: ${p => p.theme.tokens.background.transparent.accent.muted};
321-
`;

static/app/views/explore/metrics/metricInfoTabs/index.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -42,17 +42,15 @@ export function MetricInfoTabs({
4242

4343
const hasMetricsUIRefresh = canUseMetricsUIRefresh(organization);
4444

45-
const size = hasMetricsUIRefresh ? 'md' : 'xs';
46-
4745
return (
4846
<TabStateProvider<Mode>
4947
value={queryParamsMode}
5048
onChange={mode => {
5149
setAggregatesMode(mode);
5250
}}
53-
size={size}
51+
size={hasMetricsUIRefresh ? 'md' : 'xs'}
5452
>
55-
{(orientation === 'right' || visualize.visible) && (
53+
{orientation === 'right' || visualize.visible ? (
5654
<Flex direction="row" justify="between" align="center" paddingRight="xl">
5755
<TabListWrapper orientation={orientation}>
5856
<TabList variant="floating">
@@ -66,8 +64,8 @@ export function MetricInfoTabs({
6664
</TabListWrapper>
6765
{additionalActions}
6866
</Flex>
69-
)}
70-
{visualize.visible && !contentsHidden && (
67+
) : null}
68+
{visualize.visible && !contentsHidden ? (
7169
<BodyContainer>
7270
<StyledTabPanels>
7371
<TabPanels.Item key={Mode.AGGREGATE}>
@@ -84,7 +82,7 @@ export function MetricInfoTabs({
8482
</TabPanels.Item>
8583
</StyledTabPanels>
8684
</BodyContainer>
87-
)}
85+
) : null}
8886
</TabStateProvider>
8987
);
9088
}

static/app/views/explore/metrics/metricPanel/index.tsx

Lines changed: 13 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {useState} from 'react';
22

3-
import {Stack} from '@sentry/scraps/layout';
3+
import {Container, Stack} from '@sentry/scraps/layout';
44

55
import {Panel} from 'sentry/components/panels/panel';
66
import {PanelBody} from 'sentry/components/panels/panelBody';
@@ -17,13 +17,12 @@ import {useMetricAggregatesTable} from 'sentry/views/explore/metrics/hooks/useMe
1717
import {useMetricSamplesTable} from 'sentry/views/explore/metrics/hooks/useMetricSamplesTable';
1818
import {useMetricTimeseries} from 'sentry/views/explore/metrics/hooks/useMetricTimeseries';
1919
import {useTableOrientationControl} from 'sentry/views/explore/metrics/hooks/useOrientationControl';
20-
import {MetricsGraph} from 'sentry/views/explore/metrics/metricGraph';
21-
import {MetricInfoTabs} from 'sentry/views/explore/metrics/metricInfoTabs';
2220
import {SideBySideOrientation} from 'sentry/views/explore/metrics/metricPanel/sideBySideOrientation';
2321
import {StackedOrientation} from 'sentry/views/explore/metrics/metricPanel/stackedOrientation';
2422
import {type TraceMetric} from 'sentry/views/explore/metrics/metricQuery';
2523
import {canUseMetricsUIRefresh} from 'sentry/views/explore/metrics/metricsFlags';
2624
import {useMetricVisualize} from 'sentry/views/explore/metrics/metricsQueryParams';
25+
import {MetricToolbar} from 'sentry/views/explore/metrics/metricToolbar';
2726
import {
2827
useQueryParamsAggregateSortBys,
2928
useQueryParamsMode,
@@ -74,6 +73,7 @@ export function MetricPanel({traceMetric, queryIndex}: MetricPanelProps) {
7473
const aggregateSortBys = useQueryParamsAggregateSortBys();
7574
const [interval] = useChartInterval();
7675
const topEvents = useTopEvents();
76+
const visualize = useMetricVisualize();
7777

7878
useMetricsPanelAnalytics({
7979
interval,
@@ -88,28 +88,25 @@ export function MetricPanel({traceMetric, queryIndex}: MetricPanelProps) {
8888
panelIndex: queryIndex,
8989
});
9090

91-
const visualize = useMetricVisualize();
92-
9391
if (hasMetricsUIRefresh) {
9492
return (
9593
<Panel data-test-id="metric-panel">
9694
<PanelBody>
9795
<Stack gap="sm">
98-
<MetricsGraph
99-
timeseriesResult={timeseriesResult}
100-
orientation={orientation}
101-
isMetricOptionsEmpty={isMetricOptionsEmpty}
102-
queryIndex={queryIndex}
103-
/>
104-
{visualize.visible && (
105-
<MetricInfoTabs
96+
<Container paddingBottom={visualize.visible ? undefined : 'sm'}>
97+
<MetricToolbar traceMetric={traceMetric} queryIndex={queryIndex} />
98+
</Container>
99+
{visualize.visible ? (
100+
<SideBySideOrientation
101+
timeseriesResult={timeseriesResult}
106102
traceMetric={traceMetric}
107-
additionalActions={undefined}
108-
contentsHidden={infoContentHidden}
103+
setOrientation={setUserPreferenceOrientation}
109104
orientation={orientation}
105+
infoContentHidden={infoContentHidden}
106+
setInfoContentHidden={setInfoContentHidden}
110107
isMetricOptionsEmpty={isMetricOptionsEmpty}
111108
/>
112-
)}
109+
) : null}
113110
</Stack>
114111
</PanelBody>
115112
</Panel>

static/app/views/explore/metrics/metricPanel/sideBySideOrientation.tsx

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import {useRef} from 'react';
22

3-
import {Flex} from '@sentry/scraps/layout';
3+
import {Flex, Grid} from '@sentry/scraps/layout';
44

55
import {SplitPanel} from 'sentry/components/splitPanel';
66
import {useDimensions} from 'sentry/utils/useDimensions';
7+
import {useOrganization} from 'sentry/utils/useOrganization';
78
import type {useMetricTimeseries} from 'sentry/views/explore/metrics/hooks/useMetricTimeseries';
89
import type {TableOrientation} from 'sentry/views/explore/metrics/hooks/useOrientationControl';
910
import {MetricsGraph} from 'sentry/views/explore/metrics/metricGraph';
@@ -12,6 +13,7 @@ import {SAMPLES_PANEL_MIN_WIDTH} from 'sentry/views/explore/metrics/metricInfoTa
1213
import {HideContentButton} from 'sentry/views/explore/metrics/metricPanel/hideContentButton';
1314
import {PanelPositionSelector} from 'sentry/views/explore/metrics/metricPanel/panelPositionSelector';
1415
import type {TraceMetric} from 'sentry/views/explore/metrics/metricQuery';
16+
import {canUseMetricsUIRefresh} from 'sentry/views/explore/metrics/metricsFlags';
1517

1618
const MIN_LEFT_WIDTH = 400;
1719

@@ -37,9 +39,30 @@ export function SideBySideOrientation({
3739
timeseriesResult: ReturnType<typeof useMetricTimeseries>['result'];
3840
traceMetric: TraceMetric;
3941
}) {
42+
const organization = useOrganization();
43+
const hasMetricsUIRefresh = canUseMetricsUIRefresh(organization);
4044
const measureRef = useRef<HTMLDivElement>(null);
4145
const {width} = useDimensions({elementRef: measureRef});
4246

47+
if (hasMetricsUIRefresh) {
48+
return (
49+
<Grid columns="1fr 1fr" gap="sm">
50+
<MetricsGraph
51+
timeseriesResult={timeseriesResult}
52+
orientation={orientation}
53+
isMetricOptionsEmpty={isMetricOptionsEmpty}
54+
/>
55+
<div>
56+
<MetricInfoTabs
57+
traceMetric={traceMetric}
58+
orientation={orientation}
59+
isMetricOptionsEmpty={isMetricOptionsEmpty}
60+
/>
61+
</div>
62+
</Grid>
63+
);
64+
}
65+
4366
const hasSize = width > 0;
4467
// Default split is 65% of the available width but not less than MIN_LEFT_WIDTH
4568
// while also accommodating the desired right panel width.

static/app/views/explore/metrics/metricToolbar/index.tsx

Lines changed: 21 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {useCallback} from 'react';
22

3-
import {Container, Flex, Grid, Stack} from '@sentry/scraps/layout';
3+
import {Flex, Grid} from '@sentry/scraps/layout';
44

55
import {useOrganization} from 'sentry/utils/useOrganization';
66
import {type TraceMetric} from 'sentry/views/explore/metrics/metricQuery';
@@ -36,30 +36,37 @@ export function MetricToolbar({traceMetric, queryIndex}: MetricToolbarProps) {
3636

3737
if (canUseMetricsUIRefresh(organization)) {
3838
return (
39-
<Flex width="100%" align="start" gap="md" data-test-id="metric-toolbar">
39+
<Grid
40+
width="100%"
41+
align="center"
42+
gap="md"
43+
columns={`auto 2fr 3fr 6fr ${canRemoveMetric ? '24px' : '0'}`}
44+
data-test-id="metric-toolbar"
45+
paddingLeft="lg"
46+
paddingRight="lg"
47+
paddingTop="md"
48+
>
4049
<VisualizeLabel
4150
index={queryIndex}
4251
visualize={visualize}
4352
onClick={toggleVisibility}
4453
/>
45-
<Stack flex="1" minWidth={0} gap="sm" width="100%">
46-
<Flex minWidth={0} gap="xs" align="center">
47-
<Container width="100%" maxWidth={canRemoveMetric ? '225px' : undefined}>
48-
<MetricSelector traceMetric={traceMetric} onChange={setTraceMetric} />
49-
</Container>
50-
{canRemoveMetric && <DeleteMetricButton />}
51-
</Flex>
54+
<Flex minWidth={0}>
55+
<MetricSelector traceMetric={traceMetric} onChange={setTraceMetric} />
56+
</Flex>
57+
<Flex gap="md" minWidth={0}>
5258
<Flex flex="2 1 0" minWidth={0}>
5359
<AggregateDropdown traceMetric={traceMetric} />
5460
</Flex>
5561
<Flex flex="3 1 0" minWidth={0}>
5662
<GroupBySelector traceMetric={traceMetric} />
5763
</Flex>
58-
<Flex minWidth={0} width="100%">
59-
<Filter traceMetric={traceMetric} />
60-
</Flex>
61-
</Stack>
62-
</Flex>
64+
</Flex>
65+
<Flex minWidth={0}>
66+
<Filter traceMetric={traceMetric} />
67+
</Flex>
68+
{canRemoveMetric && <DeleteMetricButton />}
69+
</Grid>
6370
);
6471
}
6572

static/app/views/explore/metrics/metricToolbar/metricSaveAs.tsx

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,11 @@ import {t} from 'sentry/locale';
55
import {useChartInterval} from 'sentry/utils/useChartInterval';
66
import {useSaveAsMetricItems} from 'sentry/views/explore/metrics/useSaveAsMetricItems';
77

8-
export function MetricSaveAs() {
8+
interface MetricSaveAsProps {
9+
size?: 'sm' | 'md';
10+
}
11+
12+
export function MetricSaveAs({size = 'sm'}: MetricSaveAsProps) {
913
const [interval] = useChartInterval();
1014
const items = useSaveAsMetricItems({interval});
1115

@@ -16,7 +20,7 @@ export function MetricSaveAs() {
1620
if (items.length === 1 && 'onAction' in items[0]! && !('children' in items[0])) {
1721
const item = items[0];
1822
return (
19-
<Button size="sm" onClick={item.onAction} aria-label={item.textValue}>
23+
<Button size={size} onClick={item.onAction} aria-label={item.textValue}>
2024
{t('Save as')}
2125
</Button>
2226
);
@@ -28,7 +32,7 @@ export function MetricSaveAs() {
2832
trigger={triggerProps => (
2933
<Button
3034
{...triggerProps}
31-
size="sm"
35+
size={size}
3236
aria-label={t('Save as')}
3337
onClick={e => {
3438
e.stopPropagation();

0 commit comments

Comments
 (0)