Skip to content

Commit 37615e1

Browse files
authored
fix(tracemetrics): Improve infinite spinner empty state (#102983)
This also removes the '24h' condition on options as they are now downsampled. Closes LOGS-500
1 parent 9498924 commit 37615e1

File tree

11 files changed

+97
-30
lines changed

11 files changed

+97
-30
lines changed

static/app/views/explore/hooks/useMetricOptions.spec.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ describe('useMetricOptions', () => {
114114
dataset: 'tracemetrics',
115115
field: ['metric.name', 'metric.type', 'metric.unit', 'count(metric.name)'],
116116
referrer: 'api.explore.metric-options',
117+
statsPeriod: '3d',
117118
}),
118119
})
119120
);

static/app/views/explore/hooks/useMetricOptions.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,15 +48,12 @@ function metricOptionsQueryKey({
4848
query.project = projectIds.map(String);
4949
}
5050

51-
if (search && datetime) {
52-
// If searching we use the full filters in order to not miss the result.
51+
if (datetime) {
5352
Object.entries(normalizeDateTimeParams(datetime)).forEach(([key, value]) => {
5453
if (value !== undefined) {
5554
query[key] = value as string | string[];
5655
}
5756
});
58-
} else {
59-
query.statsPeriod = '24h'; // Default to a much smaller time window if not searching.
6057
}
6158

6259
return [`/organizations/${orgSlug}/events/`, {query}];
@@ -112,5 +109,13 @@ export function useMetricOptions({
112109
}
113110
}, [result.data]);
114111

115-
return result;
112+
const isMetricOptionsEmpty =
113+
!result.isFetching &&
114+
!result.isLoading &&
115+
(!result.data?.data || result.data.data.length === 0);
116+
117+
return {
118+
...result,
119+
isMetricOptionsEmpty,
120+
};
116121
}

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

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import {Fragment, useMemo} from 'react';
22

3+
import {ExternalLink} from '@sentry/scraps/link';
4+
35
import {CompactSelect} from 'sentry/components/core/compactSelect';
46
import {Tooltip} from 'sentry/components/core/tooltip';
57
import {IconClock, IconGraph} from 'sentry/icons';
6-
import {t} from 'sentry/locale';
8+
import {t, tct} from 'sentry/locale';
79
import {defined} from 'sentry/utils';
810
import {determineSeriesSampleCountAndIsSampled} from 'sentry/views/alerts/rules/metric/utils/determineSeriesSampleCount';
911
import {Widget} from 'sentry/views/dashboards/widgets/widget/widget';
@@ -29,6 +31,7 @@ import {
2931
} from 'sentry/views/explore/utils';
3032
import {ChartType} from 'sentry/views/insights/common/components/chart';
3133
import type {useSortedTimeSeries} from 'sentry/views/insights/common/queries/useSortedTimeSeries';
34+
import {GenericWidgetEmptyStateWarning} from 'sentry/views/performance/landing/widgets/components/selectableList';
3235

3336
import {WidgetWrapper} from './styles';
3437

@@ -41,6 +44,7 @@ interface MetricsGraphProps {
4144
timeseriesResult: ReturnType<typeof useSortedTimeSeries>;
4245
additionalActions?: React.ReactNode;
4346
infoContentHidden?: boolean;
47+
isMetricOptionsEmpty?: boolean;
4448
}
4549

4650
export function MetricsGraph({
@@ -49,6 +53,7 @@ export function MetricsGraph({
4953
orientation,
5054
additionalActions,
5155
infoContentHidden,
56+
isMetricOptionsEmpty,
5257
}: MetricsGraphProps) {
5358
const visualize = useMetricVisualize();
5459
const setVisualize = useSetMetricVisualize();
@@ -66,6 +71,7 @@ export function MetricsGraph({
6671
orientation={orientation}
6772
additionalActions={additionalActions}
6873
infoContentHidden={infoContentHidden}
74+
isMetricOptionsEmpty={isMetricOptionsEmpty}
6975
/>
7076
);
7177
}
@@ -84,6 +90,7 @@ function Graph({
8490
visualize,
8591
infoContentHidden,
8692
additionalActions,
93+
isMetricOptionsEmpty,
8794
}: GraphProps) {
8895
const aggregate = visualize.yAxis;
8996
const topEventsLimit = useQueryParamsTopEventsLimit();
@@ -156,14 +163,34 @@ function Graph({
156163
</Fragment>
157164
);
158165

166+
const showEmptyState = isMetricOptionsEmpty && visualize.visible;
167+
const showChart = visualize.visible && !isMetricOptionsEmpty;
168+
159169
return (
160170
<WidgetWrapper hideFooterBorder={orientation === 'bottom'}>
161171
<Widget
162172
Title={Title}
163173
Actions={Actions}
164-
Visualization={visualize.visible && <ChartVisualization chartInfo={chartInfo} />}
174+
Visualization={
175+
showEmptyState ? (
176+
<GenericWidgetEmptyStateWarning
177+
message={tct(
178+
'No metrics found for this time period. If this is unexpected, try updating your filters or [link:learn more] about how to use metrics.',
179+
{
180+
link: (
181+
<ExternalLink href="https://docs.sentry.io/product/explore/metrics/">
182+
{t('learn more')}
183+
</ExternalLink>
184+
),
185+
}
186+
)}
187+
/>
188+
) : showChart ? (
189+
<ChartVisualization chartInfo={chartInfo} />
190+
) : undefined
191+
}
165192
Footer={
166-
visualize.visible && (
193+
showChart && (
167194
<ConfidenceFooter
168195
chartInfo={chartInfo}
169196
isLoading={timeseriesResult.isFetching}

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

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import styled from '@emotion/styled';
44
import throttle from 'lodash/throttle';
55

66
import {Tooltip} from 'sentry/components/core/tooltip';
7-
import EmptyStateWarning from 'sentry/components/emptyStateWarning';
87
import LoadingIndicator from 'sentry/components/loadingIndicator';
98
import {SimpleTable} from 'sentry/components/tables/simpleTable';
109
import {IconWarning} from 'sentry/icons/iconWarning';
@@ -36,19 +35,21 @@ import {
3635
} from 'sentry/views/explore/queryParams/context';
3736
import {FieldRenderer} from 'sentry/views/explore/tables/fieldRenderer';
3837
import {TraceItemDataset} from 'sentry/views/explore/types';
38+
import {GenericWidgetEmptyStateWarning} from 'sentry/views/performance/landing/widgets/components/selectableList';
3939

4040
const RESULT_LIMIT = 50;
4141

4242
interface AggregatesTabProps {
4343
traceMetric: TraceMetric;
44+
isMetricOptionsEmpty?: boolean;
4445
}
4546

46-
export function AggregatesTab({traceMetric}: AggregatesTabProps) {
47+
export function AggregatesTab({traceMetric, isMetricOptionsEmpty}: AggregatesTabProps) {
4748
const topEvents = useTopEvents();
4849
const tableRef = useRef<HTMLDivElement>(null);
4950

5051
const {result, eventView, fields} = useMetricAggregatesTable({
51-
enabled: Boolean(traceMetric.name),
52+
enabled: Boolean(traceMetric.name) && !isMetricOptionsEmpty,
5253
limit: RESULT_LIMIT,
5354
traceMetric,
5455
});
@@ -151,9 +152,11 @@ export function AggregatesTab({traceMetric}: AggregatesTabProps) {
151152
};
152153
}, [result.data, fields.length]);
153154

155+
const isPending = result.isPending && !isMetricOptionsEmpty;
156+
154157
return (
155158
<StickyCompatibleSimpleTable ref={tableRef} style={tableStyle}>
156-
{result.isPending && <TransparentLoadingMask />}
159+
{isPending && <TransparentLoadingMask />}
157160

158161
<StickyCompatibleStyledHeader>
159162
{fields.map((field, i) => {
@@ -221,15 +224,13 @@ export function AggregatesTab({traceMetric}: AggregatesTabProps) {
221224
))}
222225
</SimpleTable.Row>
223226
))
224-
) : result.isPending ? (
227+
) : isPending ? (
225228
<SimpleTable.Empty>
226229
<LoadingIndicator />
227230
</SimpleTable.Empty>
228231
) : (
229232
<SimpleTable.Empty>
230-
<EmptyStateWarning>
231-
<p>{t('No aggregates found')}</p>
232-
</EmptyStateWarning>
233+
<GenericWidgetEmptyStateWarning title={t('No aggregates found')} message="" />
233234
</SimpleTable.Empty>
234235
)}
235236
</StickyCompatibleTableBody>

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

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,15 @@ interface MetricInfoTabsProps {
2323
traceMetric: TraceMetric;
2424
additionalActions?: React.ReactNode;
2525
contentsHidden?: boolean;
26+
isMetricOptionsEmpty?: boolean;
2627
}
2728

2829
export default function MetricInfoTabs({
2930
traceMetric,
3031
additionalActions,
3132
contentsHidden,
3233
orientation,
34+
isMetricOptionsEmpty,
3335
}: MetricInfoTabsProps) {
3436
const visualize = useMetricVisualize();
3537
const queryParamsMode = useQueryParamsMode();
@@ -61,10 +63,16 @@ export default function MetricInfoTabs({
6163
<BodyContainer>
6264
<StyledTabPanels>
6365
<TabPanels.Item key={Mode.AGGREGATE}>
64-
<AggregatesTab traceMetric={traceMetric} />
66+
<AggregatesTab
67+
traceMetric={traceMetric}
68+
isMetricOptionsEmpty={isMetricOptionsEmpty}
69+
/>
6570
</TabPanels.Item>
6671
<TabPanels.Item key={Mode.SAMPLES}>
67-
<SamplesTab traceMetric={traceMetric} />
72+
<SamplesTab
73+
traceMetric={traceMetric}
74+
isMetricOptionsEmpty={isMetricOptionsEmpty}
75+
/>
6876
</TabPanels.Item>
6977
</StyledTabPanels>
7078
</BodyContainer>

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

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {useMemo} from 'react';
22
import styled from '@emotion/styled';
33

4-
import EmptyStateWarning from 'sentry/components/emptyStateWarning';
54
import LoadingIndicator from 'sentry/components/loadingIndicator';
65
import {SimpleTable} from 'sentry/components/tables/simpleTable';
76
import {IconWarning} from 'sentry/icons';
@@ -22,6 +21,7 @@ import {SampleTableRow} from 'sentry/views/explore/metrics/metricInfoTabs/metric
2221
import type {TraceMetric} from 'sentry/views/explore/metrics/metricQuery';
2322
import {TraceMetricKnownFieldKey} from 'sentry/views/explore/metrics/types';
2423
import {getMetricTableColumnType} from 'sentry/views/explore/metrics/utils';
24+
import {GenericWidgetEmptyStateWarning} from 'sentry/views/performance/landing/widgets/components/selectableList';
2525

2626
const RESULT_LIMIT = 50;
2727
const TWO_MINUTE_DELAY = 120;
@@ -31,12 +31,14 @@ export const SAMPLES_PANEL_MIN_WIDTH = 350;
3131

3232
interface MetricsSamplesTableProps {
3333
embedded?: boolean;
34+
isMetricOptionsEmpty?: boolean;
3435
traceMetric?: TraceMetric;
3536
}
3637

3738
export function MetricsSamplesTable({
3839
traceMetric,
3940
embedded = false,
41+
isMetricOptionsEmpty,
4042
}: MetricsSamplesTableProps) {
4143
const columns = embedded ? TraceSamplesTableEmbeddedColumns : TraceSamplesTableColumns;
4244
const fields = columns.filter(c => getMetricTableColumnType(c) !== 'stat');
@@ -47,7 +49,7 @@ export function MetricsSamplesTable({
4749
error,
4850
isFetching,
4951
} = useMetricSamplesTable({
50-
disabled: embedded ? false : !traceMetric?.name,
52+
disabled: embedded ? false : !traceMetric?.name || isMetricOptionsEmpty,
5153
limit: RESULT_LIMIT,
5254
traceMetric,
5355
fields,
@@ -92,9 +94,7 @@ export function MetricsSamplesTable({
9294
</SimpleTable.Empty>
9395
) : (
9496
<SimpleTable.Empty>
95-
<EmptyStateWarning>
96-
<p>{t('No samples found')}</p>
97-
</EmptyStateWarning>
97+
<GenericWidgetEmptyStateWarning title={t('No samples found')} message="" />
9898
</SimpleTable.Empty>
9999
)}
100100
</StyledSimpleTableBody>

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

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,14 @@ import type {TraceMetric} from 'sentry/views/explore/metrics/metricQuery';
33

44
interface SamplesTabProps {
55
traceMetric: TraceMetric;
6+
isMetricOptionsEmpty?: boolean;
67
}
78

8-
export function SamplesTab({traceMetric}: SamplesTabProps) {
9-
return <MetricsSamplesTable traceMetric={traceMetric} />;
9+
export function SamplesTab({traceMetric, isMetricOptionsEmpty}: SamplesTabProps) {
10+
return (
11+
<MetricsSamplesTable
12+
traceMetric={traceMetric}
13+
isMetricOptionsEmpty={isMetricOptionsEmpty}
14+
/>
15+
);
1016
}

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

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Panel from 'sentry/components/panels/panel';
44
import PanelBody from 'sentry/components/panels/panelBody';
55
import {useMetricsPanelAnalytics} from 'sentry/views/explore/hooks/useAnalytics';
66
import {useChartInterval} from 'sentry/views/explore/hooks/useChartInterval';
7+
import {useMetricOptions} from 'sentry/views/explore/hooks/useMetricOptions';
78
import {useTopEvents} from 'sentry/views/explore/hooks/useTopEvents';
89
import {TraceSamplesTableColumns} from 'sentry/views/explore/metrics/constants';
910
import {useMetricAggregatesTable} from 'sentry/views/explore/metrics/hooks/useMetricAggregatesTable';
@@ -35,24 +36,25 @@ export function MetricPanel({traceMetric, queryIndex}: MetricPanelProps) {
3536
canChangeOrientation,
3637
} = useTableOrientationControl();
3738
const [infoContentHidden, setInfoContentHidden] = useState(false);
39+
const {isMetricOptionsEmpty} = useMetricOptions({enabled: Boolean(traceMetric.name)});
3840
const {result: timeseriesResult} = useMetricTimeseries({
3941
traceMetric,
40-
enabled: Boolean(traceMetric.name),
42+
enabled: Boolean(traceMetric.name) && !isMetricOptionsEmpty,
4143
});
4244

4345
const columns = TraceSamplesTableColumns;
4446
const fields = columns.filter(c => getMetricTableColumnType(c) !== 'stat');
4547

4648
const metricSamplesTableResult = useMetricSamplesTable({
47-
disabled: !traceMetric?.name,
49+
disabled: !traceMetric?.name || isMetricOptionsEmpty,
4850
limit: RESULT_LIMIT,
4951
traceMetric,
5052
fields,
5153
ingestionDelaySeconds: TWO_MINUTE_DELAY,
5254
});
5355

5456
const metricAggregatesTableResult = useMetricAggregatesTable({
55-
enabled: Boolean(traceMetric.name),
57+
enabled: Boolean(traceMetric.name) && !isMetricOptionsEmpty,
5658
limit: RESULT_LIMIT,
5759
traceMetric,
5860
});
@@ -87,6 +89,7 @@ export function MetricPanel({traceMetric, queryIndex}: MetricPanelProps) {
8789
orientation={orientation}
8890
infoContentHidden={infoContentHidden}
8991
setInfoContentHidden={setInfoContentHidden}
92+
isMetricOptionsEmpty={isMetricOptionsEmpty}
9093
/>
9194
) : (
9295
<StackedOrientation
@@ -98,6 +101,7 @@ export function MetricPanel({traceMetric, queryIndex}: MetricPanelProps) {
98101
canChangeOrientation={canChangeOrientation}
99102
infoContentHidden={infoContentHidden}
100103
setInfoContentHidden={setInfoContentHidden}
104+
isMetricOptionsEmpty={isMetricOptionsEmpty}
101105
/>
102106
)}
103107
</PanelBody>

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,10 @@ export function SideBySideOrientation({
2727
setOrientation,
2828
infoContentHidden,
2929
setInfoContentHidden,
30+
isMetricOptionsEmpty,
3031
}: {
3132
infoContentHidden: boolean;
33+
isMetricOptionsEmpty: boolean;
3234
orientation: TableOrientation;
3335
queryIndex: number;
3436
setInfoContentHidden: (hidden: boolean) => void;
@@ -71,6 +73,7 @@ export function SideBySideOrientation({
7173
orientation={orientation}
7274
additionalActions={additionalActions}
7375
infoContentHidden={infoContentHidden}
76+
isMetricOptionsEmpty={isMetricOptionsEmpty}
7477
/>
7578
</div>
7679
);
@@ -87,6 +90,7 @@ export function SideBySideOrientation({
8790
timeseriesResult={timeseriesResult}
8891
queryIndex={queryIndex}
8992
orientation={orientation}
93+
isMetricOptionsEmpty={isMetricOptionsEmpty}
9094
/>
9195
),
9296
default: defaultSplit,
@@ -98,6 +102,7 @@ export function SideBySideOrientation({
98102
traceMetric={traceMetric}
99103
additionalActions={additionalActions}
100104
orientation={orientation}
105+
isMetricOptionsEmpty={isMetricOptionsEmpty}
101106
/>
102107
}
103108
/>

0 commit comments

Comments
 (0)