123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217 |
- import {useMemo} from 'react';
- import {type Theme, useTheme} from '@emotion/react';
- import {
- LineChart,
- LineChartProps,
- LineChartSeries,
- } from 'sentry/components/charts/lineChart';
- import {t} from 'sentry/locale';
- import {tooltipFormatter} from 'sentry/utils/discover/charts';
- import {makeFormatter} from 'sentry/utils/profiling/units/units';
- const durationFormatter = makeFormatter('nanoseconds', 0);
- function asSeries(
- seriesName: string,
- color: string | undefined,
- data: {timestamp: number; value: number}[]
- ) {
- return {
- data: data.map(p => ({
- name: p.timestamp * 1e3,
- value: p.value / 1e6 ?? 0,
- })),
- color,
- seriesName,
- };
- }
- function getTooltipFormatter(label: string, baseline: number) {
- return [
- '<div class="tooltip-series tooltip-series-solo">',
- '<div>',
- `<span class="tooltip-label"><strong>${label}</strong></span>`,
- tooltipFormatter(baseline / 1e6, 'duration'),
- '</div>',
- '</div>',
- '<div class="tooltip-arrow"></div>',
- ].join('');
- }
- interface BaseSparklineChartProps {
- name: string;
- points: {timestamp: number; value: number}[];
- chartProps?: Partial<LineChartProps>;
- color?: string;
- }
- interface ProfilingSparklineChartPropsWithBreakpoint extends BaseSparklineChartProps {
- aggregate_range_1: number;
- aggregate_range_2: number;
- breakpoint: number;
- end: number;
- start: number;
- }
- interface ProfilingSparklineChartPropsWithoutBreakpoint extends BaseSparklineChartProps {}
- function isBreakPointProps(
- props: ProfilingSparklineChartProps
- ): props is ProfilingSparklineChartPropsWithBreakpoint {
- return typeof (props as any).breakpoint === 'number';
- }
- type ProfilingSparklineChartProps =
- | ProfilingSparklineChartPropsWithBreakpoint
- | ProfilingSparklineChartPropsWithoutBreakpoint;
- function makeSeriesBeforeAfterLines(
- start: number,
- breakpoint: number,
- end: number,
- aggregate_range_1: number,
- aggregate_range_2: number,
- theme: Theme
- ): LineChartSeries[] {
- const dividingLine = {
- data: [],
- color: theme.textColor,
- seriesName: 'dividing line',
- markLine: {},
- };
- dividingLine.markLine = {
- data: [{xAxis: breakpoint * 1e3}],
- label: {show: false},
- lineStyle: {
- color: theme.textColor,
- type: 'solid',
- width: 2,
- },
- symbol: ['none', 'none'],
- tooltip: {
- show: false,
- },
- silent: true,
- };
- const beforeLine = {
- data: [],
- color: theme.textColor,
- seriesName: 'before line',
- markLine: {},
- };
- beforeLine.markLine = {
- data: [
- [
- {value: 'Past', coord: [start * 1e3, aggregate_range_1 / 1e6]},
- {coord: [breakpoint * 1e3, aggregate_range_1 / 1e6]},
- ],
- ],
- label: {
- show: false,
- },
- lineStyle: {
- color: theme.textColor,
- type: 'dashed',
- width: 1,
- },
- symbol: ['none', 'none'],
- tooltip: {
- formatter: getTooltipFormatter(t('Past Baseline'), aggregate_range_1),
- },
- };
- const afterLine = {
- data: [],
- color: theme.textColor,
- seriesName: 'after line',
- markLine: {},
- };
- afterLine.markLine = {
- data: [
- [
- {
- value: 'Present',
- coord: [breakpoint * 1e3, aggregate_range_2 / 1e6],
- },
- {coord: [end * 1e3, aggregate_range_2 / 1e6]},
- ],
- ],
- label: {
- show: false,
- },
- lineStyle: {
- color: theme.textColor,
- type: 'dashed',
- width: 1,
- },
- symbol: ['none', 'none'],
- tooltip: {
- formatter: getTooltipFormatter(t('Present Baseline'), aggregate_range_2),
- },
- };
- return [dividingLine, beforeLine, afterLine];
- }
- export function ProfilingSparklineChart(props: ProfilingSparklineChartProps) {
- const theme = useTheme();
- const chartProps: LineChartProps = useMemo(() => {
- const additionalSeries: LineChartSeries[] = [];
- if (isBreakPointProps(props)) {
- additionalSeries.push(
- ...makeSeriesBeforeAfterLines(
- props.start,
- props.breakpoint,
- props.end,
- props.aggregate_range_1,
- props.aggregate_range_2,
- theme
- )
- );
- }
- const baseProps: LineChartProps = {
- height: 26,
- width: 'auto',
- series: [asSeries(props.name, props.color, props.points), ...additionalSeries],
- grid: [
- {
- containLabel: false,
- top: '2px',
- left: '2px',
- right: '2px',
- bottom: '2px',
- },
- {
- containLabel: false,
- top: '2px',
- left: '2px',
- right: '2px',
- bottom: '2px',
- },
- ],
- tooltip: {
- valueFormatter: (v: number) => durationFormatter(v),
- },
- axisPointer: {},
- xAxes: [
- {
- gridIndex: 0,
- type: 'time' as const,
- show: false,
- },
- ],
- yAxes: [
- {
- scale: true,
- show: false,
- },
- ],
- ...(props.chartProps ? props.chartProps : {}),
- };
- return baseProps;
- }, [props, theme]);
- return <LineChart {...chartProps} isGroupedByDate showTimeInTooltip />;
- }
|