Skip to content

Commit f5f1448

Browse files
authored
feat(aci): Add an open in explore button to metric detector charts (#104301)
1 parent 984a288 commit f5f1448

File tree

3 files changed

+547
-19
lines changed

3 files changed

+547
-19
lines changed

static/app/views/detectors/components/details/metric/chart.tsx

Lines changed: 84 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
11
import {useMemo} from 'react';
22
import {type Theme} from '@emotion/react';
3-
import styled from '@emotion/styled';
43
import type {YAXisComponentOption} from 'echarts';
54

5+
import Feature from 'sentry/components/acl/feature';
66
import {AreaChart, type AreaChartProps} from 'sentry/components/charts/areaChart';
77
import {defaultFormatAxisLabel} from 'sentry/components/charts/components/tooltip';
88
import ErrorPanel from 'sentry/components/charts/errorPanel';
99
import {useChartZoom} from 'sentry/components/charts/useChartZoom';
1010
import {Alert} from 'sentry/components/core/alert';
11-
import {Flex} from 'sentry/components/core/layout';
11+
import {LinkButton} from 'sentry/components/core/button/linkButton';
12+
import {Container, Flex} from 'sentry/components/core/layout';
1213
import {normalizeDateTimeParams} from 'sentry/components/organizations/pageFilters/parse';
1314
import Placeholder from 'sentry/components/placeholder';
1415
import {IconWarning} from 'sentry/icons';
1516
import {t} from 'sentry/locale';
1617
import {space} from 'sentry/styles/space';
1718
import type {GroupOpenPeriod} from 'sentry/types/group';
1819
import type {MetricDetector, SnubaQuery} from 'sentry/types/workflowEngine/detectors';
20+
import {decodeScalar} from 'sentry/utils/queryString';
1921
import type RequestError from 'sentry/utils/requestError/requestError';
2022
import {useLocation} from 'sentry/utils/useLocation';
2123
import {useNavigate} from 'sentry/utils/useNavigate';
24+
import useOrganization from 'sentry/utils/useOrganization';
2225
import {
2326
buildDetectorZoomQuery,
2427
computeZoomRangeMs,
2528
} from 'sentry/views/detectors/components/details/common/buildDetectorZoomQuery';
29+
import {getDetectorOpenInDestination} from 'sentry/views/detectors/components/details/metric/getDetectorOpenInDestination';
2630
import {useDetectorChartAxisBounds} from 'sentry/views/detectors/components/details/metric/utils/useDetectorChartAxisBounds';
2731
import {getDatasetConfig} from 'sentry/views/detectors/datasetConfig/getDatasetConfig';
2832
import {getDetectorDataset} from 'sentry/views/detectors/datasetConfig/getDetectorDataset';
@@ -370,10 +374,73 @@ export function useMetricDetectorChart({
370374
};
371375
}
372376

377+
interface OpenInButtonProps {
378+
detector: MetricDetector;
379+
}
380+
381+
function OpenInButton({detector}: OpenInButtonProps) {
382+
const organization = useOrganization();
383+
const location = useLocation();
384+
const destination = getDetectorOpenInDestination({
385+
detector,
386+
organization,
387+
statsPeriod: decodeScalar(location.query.statsPeriod),
388+
start: decodeScalar(location.query.start),
389+
end: decodeScalar(location.query.end),
390+
});
391+
392+
if (!destination?.to) {
393+
return null;
394+
}
395+
396+
return (
397+
<Feature features="visibility-explore-view">
398+
<LinkButton size="xs" to={destination.to}>
399+
{destination.buttonText}
400+
</LinkButton>
401+
</Feature>
402+
);
403+
}
404+
405+
function ChartContainer({
406+
children,
407+
overflow,
408+
}: {
409+
children: React.ReactNode;
410+
overflow?: 'hidden';
411+
}) {
412+
return (
413+
<Container border="muted" radius="md" overflow={overflow}>
414+
{children}
415+
</Container>
416+
);
417+
}
418+
419+
function ChartBody({children}: {children: React.ReactNode}) {
420+
return <Container padding="lg">{children}</Container>;
421+
}
422+
423+
function ChartFooter({detector}: {detector: MetricDetector}) {
424+
return (
425+
<Flex justify="end" padding="lg" borderTop="muted">
426+
<OpenInButton detector={detector} />
427+
</Flex>
428+
);
429+
}
430+
373431
export function MetricDetectorDetailsChart({detector}: MetricDetectorDetailsChartProps) {
374432
const location = useLocation();
433+
const organization = useOrganization();
375434
const dateParams = normalizeDateTimeParams(location.query);
376435

436+
const destination = getDetectorOpenInDestination({
437+
detector,
438+
organization,
439+
statsPeriod: decodeScalar(location.query.statsPeriod),
440+
start: decodeScalar(location.query.start),
441+
end: decodeScalar(location.query.end),
442+
});
443+
377444
const {data: openPeriods = []} = useOpenPeriods({
378445
detectorId: detector.id,
379446
...dateParams,
@@ -388,47 +455,45 @@ export function MetricDetectorDetailsChart({detector}: MetricDetectorDetailsChar
388455

389456
if (isLoading) {
390457
return (
391-
<Flex height={CHART_HEIGHT} justify="center" align="center">
392-
<Placeholder height={`${CHART_HEIGHT}px`} />
393-
</Flex>
458+
<ChartContainer>
459+
<ChartBody>
460+
<Flex height={CHART_HEIGHT} justify="center" align="center">
461+
<Placeholder height={`${CHART_HEIGHT}px`} />
462+
</Flex>
463+
</ChartBody>
464+
{destination && <ChartFooter detector={detector} />}
465+
</ChartContainer>
394466
);
395467
}
396468
if (error || !chartProps) {
397469
const errorMessage =
398470
typeof error?.responseJSON?.detail === 'string' ? error.responseJSON.detail : null;
399471
return (
400-
<ChartContainer style={{overflow: 'hidden'}}>
472+
<ChartContainer overflow="hidden">
401473
{errorMessage && (
402474
<Alert system type="error">
403475
{errorMessage}
404476
</Alert>
405477
)}
406-
<ChartContainerBody>
478+
<ChartBody>
407479
<Flex justify="center" align="center">
408480
<ErrorPanel height={`${CHART_HEIGHT - 45}px`}>
409481
<IconWarning color="gray300" size="lg" />
410482
<div>{t('Error loading chart data')}</div>
411483
</ErrorPanel>
412484
</Flex>
413-
</ChartContainerBody>
485+
</ChartBody>
486+
{destination && <ChartFooter detector={detector} />}
414487
</ChartContainer>
415488
);
416489
}
417490

418491
return (
419492
<ChartContainer>
420-
<ChartContainerBody>
493+
<ChartBody>
421494
<AreaChart {...chartProps} />
422-
</ChartContainerBody>
495+
</ChartBody>
496+
{destination && <ChartFooter detector={detector} />}
423497
</ChartContainer>
424498
);
425499
}
426-
427-
const ChartContainer = styled('div')`
428-
border: 1px solid ${p => p.theme.border};
429-
border-radius: ${p => p.theme.borderRadius};
430-
`;
431-
432-
const ChartContainerBody = styled('div')`
433-
padding: ${p => p.theme.space.xs} ${p => p.theme.space.lg} ${p => p.theme.space.xs};
434-
`;

0 commit comments

Comments
 (0)