|
@@ -1,113 +1,149 @@
|
|
|
+import {useMemo} from 'react';
|
|
|
import {useTheme} from '@emotion/react';
|
|
|
|
|
|
+import BaseChart from 'sentry/components/charts/baseChart';
|
|
|
import ChartZoom from 'sentry/components/charts/chartZoom';
|
|
|
-import VisualMap from 'sentry/components/charts/components/visualMap';
|
|
|
-import {
|
|
|
- LineChart as EchartsLineChart,
|
|
|
- LineChartProps,
|
|
|
-} from 'sentry/components/charts/lineChart';
|
|
|
-import {EventsStatsData} from 'sentry/types';
|
|
|
+import BarSeries from 'sentry/components/charts/series/barSeries';
|
|
|
+import LineSeries from 'sentry/components/charts/series/lineSeries';
|
|
|
+import {Series} from 'sentry/types/echarts';
|
|
|
import {getUserTimezone} from 'sentry/utils/dates';
|
|
|
import {
|
|
|
axisLabelFormatter,
|
|
|
getDurationUnit,
|
|
|
tooltipFormatter,
|
|
|
} from 'sentry/utils/discover/charts';
|
|
|
-import {aggregateOutputType} from 'sentry/utils/discover/fields';
|
|
|
+import {aggregateOutputType, RateUnits} from 'sentry/utils/discover/fields';
|
|
|
import useRouter from 'sentry/utils/useRouter';
|
|
|
-import {transformEventStats} from 'sentry/views/performance/trends/chart';
|
|
|
import {NormalizedTrendsTransaction} from 'sentry/views/performance/trends/types';
|
|
|
import {getIntervalLine} from 'sentry/views/performance/utils';
|
|
|
|
|
|
interface ChartProps {
|
|
|
- chartLabel: string;
|
|
|
end: string;
|
|
|
evidenceData: NormalizedTrendsTransaction;
|
|
|
+ percentileSeries: Series[];
|
|
|
start: string;
|
|
|
- statsData: EventsStatsData;
|
|
|
+ throughputSeries: Series;
|
|
|
}
|
|
|
|
|
|
-function LineChart({statsData, evidenceData, start, end, chartLabel}: ChartProps) {
|
|
|
+function LineChart({
|
|
|
+ percentileSeries,
|
|
|
+ throughputSeries,
|
|
|
+ evidenceData,
|
|
|
+ start,
|
|
|
+ end,
|
|
|
+}: ChartProps) {
|
|
|
const theme = useTheme();
|
|
|
const router = useRouter();
|
|
|
|
|
|
- const resultSeries = transformEventStats(statsData, chartLabel);
|
|
|
+ const leftSeries = useMemo(() => {
|
|
|
+ const needsLabel = true;
|
|
|
+ const intervalSeries = getIntervalLine(
|
|
|
+ theme,
|
|
|
+ percentileSeries || [],
|
|
|
+ 0.5,
|
|
|
+ needsLabel,
|
|
|
+ evidenceData,
|
|
|
+ true
|
|
|
+ );
|
|
|
+ return [
|
|
|
+ ...percentileSeries,
|
|
|
+ ...intervalSeries.filter(s => !s.markArea), // get rid of the shading
|
|
|
+ ];
|
|
|
+ }, [percentileSeries, evidenceData, theme]);
|
|
|
|
|
|
- const needsLabel = true;
|
|
|
- const intervalSeries = getIntervalLine(
|
|
|
- theme,
|
|
|
- resultSeries || [],
|
|
|
- 0.5,
|
|
|
- needsLabel,
|
|
|
- evidenceData,
|
|
|
- true
|
|
|
- );
|
|
|
+ const rightSeries = useMemo(() => [throughputSeries], [throughputSeries]);
|
|
|
+
|
|
|
+ const series = useMemo(() => {
|
|
|
+ return [
|
|
|
+ ...rightSeries.map(({seriesName, data, ...options}) =>
|
|
|
+ BarSeries({
|
|
|
+ ...options,
|
|
|
+ name: seriesName,
|
|
|
+ data: data?.map(({value, name, itemStyle}) => {
|
|
|
+ if (itemStyle === undefined) {
|
|
|
+ return [name, value];
|
|
|
+ }
|
|
|
+ return {value: [name, value], itemStyle};
|
|
|
+ }),
|
|
|
+ animation: false,
|
|
|
+ animationThreshold: 1,
|
|
|
+ animationDuration: 0,
|
|
|
+ yAxisIndex: 1,
|
|
|
+ })
|
|
|
+ ),
|
|
|
+ ...leftSeries.map(({seriesName, data, ...options}) =>
|
|
|
+ LineSeries({
|
|
|
+ ...options,
|
|
|
+ name: seriesName,
|
|
|
+ data: data?.map(({value, name}) => [name, value]),
|
|
|
+ animation: false,
|
|
|
+ animationThreshold: 1,
|
|
|
+ animationDuration: 0,
|
|
|
+ showSymbol: false,
|
|
|
+ yAxisIndex: 0,
|
|
|
+ })
|
|
|
+ ),
|
|
|
+ ];
|
|
|
+ }, [leftSeries, rightSeries]);
|
|
|
|
|
|
- const series = [...resultSeries, ...intervalSeries];
|
|
|
+ const chartOptions: Omit<
|
|
|
+ React.ComponentProps<typeof BaseChart>,
|
|
|
+ 'series'
|
|
|
+ > = useMemo(() => {
|
|
|
+ const legend = {
|
|
|
+ right: 16,
|
|
|
+ top: 12,
|
|
|
+ data: [...percentileSeries.map(s => s.seriesName), throughputSeries.seriesName],
|
|
|
+ };
|
|
|
+
|
|
|
+ const durationUnit = getDurationUnit(leftSeries);
|
|
|
+
|
|
|
+ const yAxes: React.ComponentProps<typeof BaseChart>['yAxes'] = [
|
|
|
+ {
|
|
|
+ minInterval: durationUnit,
|
|
|
+ axisLabel: {
|
|
|
+ color: theme.chartLabel,
|
|
|
+ formatter: (value: number) =>
|
|
|
+ axisLabelFormatter(value, 'duration', undefined, durationUnit),
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ];
|
|
|
|
|
|
- const durationUnit = getDurationUnit(series);
|
|
|
+ if (rightSeries.length) {
|
|
|
+ yAxes.push({
|
|
|
+ axisLabel: {
|
|
|
+ color: theme.chartLabel,
|
|
|
+ formatter: (value: number) =>
|
|
|
+ axisLabelFormatter(value, 'rate', true, undefined, RateUnits.PER_SECOND),
|
|
|
+ },
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- const chartOptions: Omit<LineChartProps, 'series'> = {
|
|
|
- tooltip: {
|
|
|
- valueFormatter: (value, seriesName) => {
|
|
|
- return tooltipFormatter(value, aggregateOutputType(seriesName));
|
|
|
+ return {
|
|
|
+ colors: [theme.gray200, theme.gray500],
|
|
|
+ grid: {
|
|
|
+ left: '10px',
|
|
|
+ right: '10px',
|
|
|
+ top: '40px',
|
|
|
+ bottom: '0px',
|
|
|
},
|
|
|
- },
|
|
|
- yAxis: {
|
|
|
- minInterval: durationUnit,
|
|
|
- axisLabel: {
|
|
|
- color: theme.chartLabel,
|
|
|
- formatter: (value: number) =>
|
|
|
- axisLabelFormatter(value, 'duration', undefined, durationUnit),
|
|
|
+ legend,
|
|
|
+ toolBox: {show: false},
|
|
|
+ tooltip: {
|
|
|
+ valueFormatter: (value, seriesName) => {
|
|
|
+ return tooltipFormatter(value, aggregateOutputType(seriesName));
|
|
|
+ },
|
|
|
},
|
|
|
- },
|
|
|
- };
|
|
|
+ xAxis: {type: 'time'},
|
|
|
+ yAxes,
|
|
|
+ };
|
|
|
+ }, [theme, leftSeries, rightSeries, percentileSeries, throughputSeries]);
|
|
|
|
|
|
return (
|
|
|
<ChartZoom router={router} start={start} end={end} utc={getUserTimezone() === 'UTC'}>
|
|
|
- {zoomRenderProps => {
|
|
|
- return (
|
|
|
- <EchartsLineChart
|
|
|
- {...zoomRenderProps}
|
|
|
- {...chartOptions}
|
|
|
- series={series}
|
|
|
- seriesOptions={{
|
|
|
- showSymbol: false,
|
|
|
- }}
|
|
|
- toolBox={{
|
|
|
- show: false,
|
|
|
- }}
|
|
|
- grid={{
|
|
|
- left: '10px',
|
|
|
- right: '10px',
|
|
|
- top: '20px',
|
|
|
- bottom: '0px',
|
|
|
- }}
|
|
|
- options={{
|
|
|
- visualMap: VisualMap({
|
|
|
- show: false,
|
|
|
- type: 'piecewise',
|
|
|
- selectedMode: false,
|
|
|
- dimension: 0,
|
|
|
- pieces: [
|
|
|
- {
|
|
|
- gte: 0,
|
|
|
- lt: evidenceData?.breakpoint ? evidenceData.breakpoint * 1000 : 0,
|
|
|
- color: theme.gray500,
|
|
|
- },
|
|
|
- {
|
|
|
- gte: evidenceData?.breakpoint ? evidenceData.breakpoint * 1000 : 0,
|
|
|
- color: theme.red300,
|
|
|
- },
|
|
|
- ],
|
|
|
- }),
|
|
|
- }}
|
|
|
- xAxis={{
|
|
|
- type: 'time',
|
|
|
- }}
|
|
|
- />
|
|
|
- );
|
|
|
- }}
|
|
|
+ {zoomRenderProps => (
|
|
|
+ <BaseChart {...zoomRenderProps} {...chartOptions} series={series} />
|
|
|
+ )}
|
|
|
</ChartZoom>
|
|
|
);
|
|
|
}
|