@@ -3,18 +3,20 @@ import {type Theme} from '@emotion/react';
33import styled from '@emotion/styled' ;
44import type { YAXisComponentOption } from 'echarts' ;
55
6- import { AreaChart } from 'sentry/components/charts/areaChart' ;
6+ import { AreaChart , type AreaChartProps } from 'sentry/components/charts/areaChart' ;
77import { defaultFormatAxisLabel } from 'sentry/components/charts/components/tooltip' ;
88import ErrorPanel from 'sentry/components/charts/errorPanel' ;
99import { useChartZoom } from 'sentry/components/charts/useChartZoom' ;
1010import { Alert } from 'sentry/components/core/alert' ;
1111import { Flex } from 'sentry/components/core/layout' ;
12+ import { normalizeDateTimeParams } from 'sentry/components/organizations/pageFilters/parse' ;
1213import Placeholder from 'sentry/components/placeholder' ;
1314import { IconWarning } from 'sentry/icons' ;
1415import { t } from 'sentry/locale' ;
1516import { space } from 'sentry/styles/space' ;
1617import type { GroupOpenPeriod } from 'sentry/types/group' ;
1718import type { MetricDetector , SnubaQuery } from 'sentry/types/workflowEngine/detectors' ;
19+ import type RequestError from 'sentry/utils/requestError/requestError' ;
1820import { useLocation } from 'sentry/utils/useLocation' ;
1921import { useNavigate } from 'sentry/utils/useNavigate' ;
2022import {
@@ -79,15 +81,16 @@ interface MetricDetectorDetailsChartProps {
7981}
8082const CHART_HEIGHT = 180 ;
8183
82- interface MetricDetectorChartProps {
84+ interface UseMetricDetectorChartProps {
8385 detector : MetricDetector ;
84- snubaQuery : SnubaQuery ;
86+ openPeriods : GroupOpenPeriod [ ] ;
8587 /**
8688 * Relative time period (e.g., '7d'). Use either statsPeriod or absolute start/end.
8789 */
88- end ?: string ;
89- start ?: string ;
90- statsPeriod ?: string ;
90+ end ?: string | null ;
91+ height ?: number ;
92+ start ?: string | null ;
93+ statsPeriod ?: string | null ;
9194}
9295
9396function createTriggerIntervalMarkerData ( {
@@ -137,18 +140,25 @@ function createOpenPeriodMarkerData({
137140 } ) ) ;
138141}
139142
140- function MetricDetectorChart ( {
143+ type UseMetricDetectorChartResult =
144+ | { chartProps : AreaChartProps ; error : null ; isLoading : false }
145+ | { chartProps : null ; error : null ; isLoading : true }
146+ | { chartProps : null ; error : RequestError ; isLoading : false } ;
147+
148+ export function useMetricDetectorChart ( {
141149 statsPeriod,
142150 start,
143151 end,
144- snubaQuery,
145152 detector,
146- } : MetricDetectorChartProps ) {
153+ openPeriods,
154+ height = CHART_HEIGHT ,
155+ } : UseMetricDetectorChartProps ) : UseMetricDetectorChartResult {
147156 const navigate = useNavigate ( ) ;
148157 const location = useLocation ( ) ;
149158 const detectionType = detector . config . detectionType ;
150159 const comparisonDelta =
151160 detectionType === 'percent' ? detector . config . comparisonDelta : undefined ;
161+ const snubaQuery = detector . dataSources [ 0 ] . queryObj . snubaQuery ;
152162 const dataset = getDetectorDataset ( snubaQuery . dataset , snubaQuery . eventTypes ) ;
153163 const datasetConfig = getDatasetConfig ( dataset ) ;
154164 const { series, comparisonSeries, isLoading, error} = useMetricDetectorSeries ( {
@@ -173,13 +183,6 @@ function MetricDetectorChart({
173183 comparisonSeries,
174184 } ) ;
175185
176- const { data : openPeriods = [ ] } = useOpenPeriods ( {
177- detectorId : detector . id ,
178- start,
179- end,
180- statsPeriod,
181- } ) ;
182-
183186 const incidentPeriods = useMemo ( ( ) => {
184187 return openPeriods . flatMap < IncidentPeriod > ( period => [
185188 createTriggerIntervalMarkerData ( {
@@ -277,10 +280,10 @@ function MetricDetectorChart({
277280
278281 return axes ;
279282 } , [
280- maxValue ,
281- openPeriodMarkerResult . incidentMarkerYAxis ,
282283 detectionType ,
283284 snubaQuery . aggregate ,
285+ maxValue ,
286+ openPeriodMarkerResult . incidentMarkerYAxis ,
284287 ] ) ;
285288
286289 const grid = useMemo ( ( ) => {
@@ -293,19 +296,93 @@ function MetricDetectorChart({
293296 } ;
294297 } , [ openPeriodMarkerResult . incidentMarkerGrid ] ) ;
295298
299+ const chartProps = useMemo < AreaChartProps | null > ( ( ) => {
300+ if ( isLoading || error ) {
301+ return null ;
302+ }
303+ return {
304+ showTimeInTooltip : true ,
305+ height,
306+ stacked : false ,
307+ series,
308+ additionalSeries,
309+ yAxes : yAxes . length > 1 ? yAxes : undefined ,
310+ yAxis : yAxes . length === 1 ? yAxes [ 0 ] : undefined ,
311+ grid,
312+ xAxis : openPeriodMarkerResult . incidentMarkerXAxis ,
313+ tooltip : {
314+ valueFormatter : getDetectorChartFormatters ( {
315+ detectionType,
316+ aggregate : snubaQuery . aggregate ,
317+ } ) . formatTooltipValue ,
318+ } ,
319+ ...chartZoomProps ,
320+ onChartReady : chart => {
321+ chartZoomProps . onChartReady ( chart ) ;
322+ openPeriodMarkerResult . onChartReady ( chart ) ;
323+ } ,
324+ } ;
325+ } , [
326+ additionalSeries ,
327+ chartZoomProps ,
328+ detectionType ,
329+ error ,
330+ grid ,
331+ height ,
332+ isLoading ,
333+ openPeriodMarkerResult ,
334+ series ,
335+ snubaQuery . aggregate ,
336+ yAxes ,
337+ ] ) ;
338+
339+ if ( chartProps ) {
340+ return {
341+ chartProps,
342+ error : null ,
343+ isLoading : false ,
344+ } ;
345+ }
346+
347+ if ( error ) {
348+ return {
349+ chartProps : null ,
350+ error,
351+ isLoading : false ,
352+ } ;
353+ }
354+
355+ return {
356+ isLoading : true ,
357+ error : null ,
358+ chartProps : null ,
359+ } ;
360+ }
361+
362+ export function MetricDetectorDetailsChart ( { detector} : MetricDetectorDetailsChartProps ) {
363+ const location = useLocation ( ) ;
364+ const dateParams = normalizeDateTimeParams ( location . query ) ;
365+
366+ const { data : openPeriods = [ ] } = useOpenPeriods ( {
367+ detectorId : detector . id ,
368+ ...dateParams ,
369+ } ) ;
370+
371+ const { chartProps, isLoading, error} = useMetricDetectorChart ( {
372+ detector,
373+ openPeriods,
374+ height : CHART_HEIGHT ,
375+ ...dateParams ,
376+ } ) ;
377+
296378 if ( isLoading ) {
297379 return (
298- < ChartContainer >
299- < ChartContainerBody >
300- < Flex height = { CHART_HEIGHT } justify = "center" align = "center" >
301- < Placeholder height = { `${ CHART_HEIGHT } px` } />
302- </ Flex >
303- </ ChartContainerBody >
304- </ ChartContainer >
380+ < Flex height = { CHART_HEIGHT } justify = "center" align = "center" >
381+ < Placeholder height = { `${ CHART_HEIGHT } px` } />
382+ </ Flex >
305383 ) ;
306384 }
307-
308- if ( error ) {
385+ if ( error || ! chartProps ) {
309386 const errorMessage =
310387 typeof error ?. responseJSON ?. detail === 'string' ? error . responseJSON . detail : null ;
311388 return (
@@ -330,53 +407,12 @@ function MetricDetectorChart({
330407 return (
331408 < ChartContainer >
332409 < ChartContainerBody >
333- < AreaChart
334- showTimeInTooltip
335- height = { CHART_HEIGHT }
336- stacked = { false }
337- series = { series }
338- additionalSeries = { additionalSeries }
339- yAxes = { yAxes . length > 1 ? yAxes : undefined }
340- yAxis = { yAxes . length === 1 ? yAxes [ 0 ] : undefined }
341- grid = { grid }
342- xAxis = { openPeriodMarkerResult . incidentMarkerXAxis }
343- tooltip = { {
344- valueFormatter : getDetectorChartFormatters ( {
345- detectionType,
346- aggregate : snubaQuery . aggregate ,
347- } ) . formatTooltipValue ,
348- } }
349- { ...chartZoomProps }
350- onChartReady = { chart => {
351- chartZoomProps . onChartReady ( chart ) ;
352- openPeriodMarkerResult . onChartReady ( chart ) ;
353- } }
354- />
410+ < AreaChart { ...chartProps } />
355411 </ ChartContainerBody >
356412 </ ChartContainer >
357413 ) ;
358414}
359415
360- export function MetricDetectorDetailsChart ( {
361- detector,
362- snubaQuery,
363- } : MetricDetectorDetailsChartProps ) {
364- const location = useLocation ( ) ;
365- const statsPeriod = location . query ?. statsPeriod as string | undefined ;
366- const start = location . query ?. start as string | undefined ;
367- const end = location . query ?. end as string | undefined ;
368- const dateParams = start && end ? { start, end} : { statsPeriod} ;
369-
370- return (
371- < MetricDetectorChart
372- detector = { detector }
373- // Pass snubaQuery separately to avoid checking null in all places
374- snubaQuery = { snubaQuery }
375- { ...dateParams }
376- />
377- ) ;
378- }
379-
380416const ChartContainer = styled ( 'div' ) `
381417 border: 1px solid ${ p => p . theme . border } ;
382418 border-radius: ${ p => p . theme . borderRadius } ;
0 commit comments