From a4d2e09bb2cea612d2914e751096e2765ec43fee Mon Sep 17 00:00:00 2001 From: Taylan Gocmen Date: Wed, 27 Oct 2021 10:18:16 -0700 Subject: [PATCH 1/6] feat(cmp_alerts): Show comparison data in dashed line in alert event stat chart --- static/app/components/charts/baseChart.tsx | 1 + .../components/charts/components/tooltip.tsx | 14 +++- .../app/components/charts/eventsRequest.tsx | 2 +- .../incidentRules/triggers/chart/index.tsx | 16 +++- .../triggers/chart/thresholdsChart.tsx | 75 +++++++++++++++++-- 5 files changed, 98 insertions(+), 10 deletions(-) diff --git a/static/app/components/charts/baseChart.tsx b/static/app/components/charts/baseChart.tsx index b39db877acde4d..ef20200ba069a9 100644 --- a/static/app/components/charts/baseChart.tsx +++ b/static/app/components/charts/baseChart.tsx @@ -112,6 +112,7 @@ type Props = { seriesParams?: EChartOption.Tooltip.Format ) => string | number; nameFormatter?: (name: string) => string; + markerFormatter?: (marker: string, label?: string) => string; /** * Array containing seriesNames that need to be indented */ diff --git a/static/app/components/charts/components/tooltip.tsx b/static/app/components/charts/components/tooltip.tsx index af73e5dfaf770d..15ab6894d3f9e5 100644 --- a/static/app/components/charts/components/tooltip.tsx +++ b/static/app/components/charts/components/tooltip.tsx @@ -60,6 +60,10 @@ function defaultNameFormatter(value: string) { return value; } +function defaultMarkerFormatter(value: string) { + return value; +} + function getSeriesValue(series: EChartOption.Tooltip.Format, offset: number) { if (!series.data) { return undefined; @@ -81,7 +85,8 @@ type TooltipFormatters = | 'filter' | 'formatAxisLabel' | 'valueFormatter' - | 'nameFormatter'; + | 'nameFormatter' + | 'markerFormatter'; type FormatterOptions = Pick, TooltipFormatters> & Pick & { @@ -105,6 +110,7 @@ function getFormatter({ bucketSize, valueFormatter = defaultValueFormatter, nameFormatter = defaultNameFormatter, + markerFormatter = defaultMarkerFormatter, indentLabels = [], addSecondsToTimeFormat = false, }: FormatterOptions) { @@ -203,11 +209,13 @@ function getFormatter({ ); const value = valueFormatter(getSeriesValue(s, 1), s.seriesName, s); + const marker = markerFormatter(s.marker ?? '', s.seriesName); + const className = indentLabels.includes(formattedLabel) ? 'tooltip-label tooltip-label-indent' : 'tooltip-label'; - return `
${s.marker} ${formattedLabel} ${value}
`; + return `
${marker} ${formattedLabel} ${value}
`; }) .join(''), '', @@ -235,6 +243,7 @@ export default function Tooltip({ formatAxisLabel, valueFormatter, nameFormatter, + markerFormatter, hideDelay, indentLabels, ...props @@ -252,6 +261,7 @@ export default function Tooltip({ formatAxisLabel, valueFormatter, nameFormatter, + markerFormatter, indentLabels, }); diff --git a/static/app/components/charts/eventsRequest.tsx b/static/app/components/charts/eventsRequest.tsx index 8dfeff961818dc..42270cbaab1389 100644 --- a/static/app/components/charts/eventsRequest.tsx +++ b/static/app/components/charts/eventsRequest.tsx @@ -69,7 +69,7 @@ type DefaultProps = { */ interval: string; /** - * Time delta for comparing intervals alert metrics, value in minutes + * Time delta for comparing intervals of alert metrics, in seconds */ comparisonDelta?: number; /** diff --git a/static/app/views/alerts/incidentRules/triggers/chart/index.tsx b/static/app/views/alerts/incidentRules/triggers/chart/index.tsx index 6cd8134bf8b5ac..93744e374a013d 100644 --- a/static/app/views/alerts/incidentRules/triggers/chart/index.tsx +++ b/static/app/views/alerts/incidentRules/triggers/chart/index.tsx @@ -1,5 +1,6 @@ import * as React from 'react'; import styled from '@emotion/styled'; +import capitalize from 'lodash/capitalize'; import chunk from 'lodash/chunk'; import maxBy from 'lodash/maxBy'; import minBy from 'lodash/minBy'; @@ -31,6 +32,7 @@ import { } from 'app/utils/sessions'; import theme from 'app/utils/theme'; import withApi from 'app/utils/withApi'; +import {COMPARISON_DELTA_OPTIONS} from 'app/views/alerts/incidentRules/constants'; import {isSessionAggregate, SESSION_AGGREGATE_TO_FIELD} from 'app/views/alerts/utils'; import {AlertWizardAlertNames} from 'app/views/alerts/wizard/options'; import {getAlertTypeFromAggregateDataset} from 'app/views/alerts/wizard/utils'; @@ -218,6 +220,13 @@ class TriggersChart extends React.PureComponent { return period; }; + get comparisonSeriesName() { + return capitalize( + COMPARISON_DELTA_OPTIONS.find(({value}) => value === this.props.comparisonDelta) + ?.label || '' + ); + } + getComparisonMarkLines( timeseriesData: Series[] = [], comparisonTimeseriesData: Series[] = [] @@ -362,6 +371,7 @@ class TriggersChart extends React.PureComponent { timeseriesData: Series[] = [], isLoading: boolean, isReloading: boolean, + comparisonData?: Series[], comparisonMarkLines?: LineChartSeries[], minutesThresholdToDisplaySeconds?: number ) { @@ -389,6 +399,8 @@ class TriggersChart extends React.PureComponent { minValue={minBy(timeseriesData[0]?.data, ({value}) => value)?.value} maxValue={maxBy(timeseriesData[0]?.data, ({value}) => value)?.value} data={timeseriesData} + comparisonData={comparisonData ?? []} + comparisonSeriesName={this.comparisonSeriesName} comparisonMarkLines={comparisonMarkLines ?? []} hideThresholdLines={comparisonType === AlertRuleComparisonType.CHANGE} triggers={triggers} @@ -475,6 +487,7 @@ class TriggersChart extends React.PureComponent { loading, reloading, undefined, + undefined, MINUTES_THRESHOLD_TO_DISPLAY_SECONDS ); }} @@ -490,7 +503,7 @@ class TriggersChart extends React.PureComponent { environment={environment ? [environment] : undefined} project={projects.map(({id}) => Number(id))} interval={`${timeWindow}m`} - comparisonDelta={comparisonDelta} + comparisonDelta={comparisonDelta && comparisonDelta * 60} period={period} yAxis={aggregate} includePrevious={false} @@ -544,6 +557,7 @@ class TriggersChart extends React.PureComponent { timeseriesData, loading, reloading, + comparisonTimeseriesData, comparisonMarkLines ); }} diff --git a/static/app/views/alerts/incidentRules/triggers/chart/thresholdsChart.tsx b/static/app/views/alerts/incidentRules/triggers/chart/thresholdsChart.tsx index 7676ef48a5b1db..d9481a51cafcfa 100644 --- a/static/app/views/alerts/incidentRules/triggers/chart/thresholdsChart.tsx +++ b/static/app/views/alerts/incidentRules/triggers/chart/thresholdsChart.tsx @@ -3,8 +3,12 @@ import color from 'color'; import debounce from 'lodash/debounce'; import flatten from 'lodash/flatten'; -import AreaChart, {AreaChartSeries} from 'app/components/charts/areaChart'; +import {AreaChartSeries} from 'app/components/charts/areaChart'; +import BaseChart from 'app/components/charts/baseChart'; import Graphic from 'app/components/charts/components/graphic'; +import {LineChartSeries} from 'app/components/charts/lineChart'; +import AreaSeries from 'app/components/charts/series/areaSeries'; +import LineSeries from 'app/components/charts/series/lineSeries'; import space from 'app/styles/space'; import {GlobalSelection} from 'app/types'; import {ReactEchartsRef, Series} from 'app/types/echarts'; @@ -21,7 +25,8 @@ import {AlertRuleThresholdType, IncidentRule, Trigger} from '../../types'; type DefaultProps = { data: Series[]; - comparisonMarkLines: AreaChartSeries[]; + comparisonData: Series[]; + comparisonMarkLines: LineChartSeries[]; }; type Props = DefaultProps & { @@ -33,6 +38,7 @@ type Props = DefaultProps & { minutesThresholdToDisplaySeconds?: number; maxValue?: number; minValue?: number; + comparisonSeriesName?: string; } & Partial; type State = { @@ -63,6 +69,7 @@ const COLOR = { export default class ThresholdsChart extends PureComponent { static defaultProps: DefaultProps = { data: [], + comparisonData: [], comparisonMarkLines: [], }; @@ -81,6 +88,7 @@ export default class ThresholdsChart extends PureComponent { if ( this.props.triggers !== prevProps.triggers || this.props.data !== prevProps.data || + this.props.comparisonData !== prevProps.comparisonData || this.props.comparisonMarkLines !== prevProps.comparisonMarkLines ) { this.handleUpdateChartAxis(); @@ -295,6 +303,8 @@ export default class ThresholdsChart extends PureComponent { triggers, period, aggregate, + comparisonData, + comparisonSeriesName, comparisonMarkLines, minutesThresholdToDisplaySeconds, } = this.props; @@ -306,6 +316,13 @@ export default class ThresholdsChart extends PureComponent { }) ); + const comparisonDataWithoutRecentBucket = comparisonData?.map( + ({data: eventData, ...restOfData}) => ({ + ...restOfData, + data: eventData.slice(0, -1), + }) + ); + // Disable all lines by default but the 1st one const selected: Record = dataWithoutRecentBucket.reduce( (acc, {seriesName}, index) => { @@ -323,8 +340,17 @@ export default class ThresholdsChart extends PureComponent { const chartOptions = { tooltip: { - valueFormatter: (value: number, seriesName?: string) => - alertTooltipValueFormatter(value, seriesName ?? '', aggregate), + // use the main aggregate for all series (main, min, max, avg, comparison) + // to format all values similarly + valueFormatter: (value: number) => + alertTooltipValueFormatter(value, aggregate, aggregate), + + markerFormatter: (marker: string, seriesName?: string) => { + if (seriesName === comparisonSeriesName) { + return ''; + } + return marker; + }, }, yAxis: { min: this.state.yAxisMin ?? undefined, @@ -337,7 +363,7 @@ export default class ThresholdsChart extends PureComponent { }; return ( - { ]) ), })} - series={[...dataWithoutRecentBucket, ...comparisonMarkLines]} + series={[ + ...dataWithoutRecentBucket.map( + ({seriesName, data: _data, ...otherSeriesProps}) => + AreaSeries({ + name: seriesName, + data: _data.map(({name, value}) => [name, value]), + lineStyle: { + opacity: 1, + width: 0.4, + }, + areaStyle: { + opacity: 1.0, + }, + animation: false, + animationThreshold: 1, + animationDuration: 0, + ...otherSeriesProps, + }) + ), + ...comparisonDataWithoutRecentBucket.map(({data: _data, ...otherSeriesProps}) => + LineSeries({ + name: comparisonSeriesName, + data: _data.map(({name, value}) => [name, value]), + lineStyle: {color: theme.gray200, type: 'dashed', width: 1}, + animation: false, + animationThreshold: 1, + animationDuration: 0, + ...otherSeriesProps, + }) + ), + ...comparisonMarkLines.map(({seriesName, data: _data, ...seriesProps}) => + LineSeries({ + name: seriesName, + data: _data.map(({name, value}) => [name, value]), + ...seriesProps, + }) + ), + ]} onFinished={() => { // We want to do this whenever the chart finishes re-rendering so that we can update the dimensions of // any graphics related to the triggers (e.g. the threshold areas + boundaries) From e7da6fe0b4cae4bbaaa94dba608a54417c1084c8 Mon Sep 17 00:00:00 2001 From: Taylan Gocmen Date: Wed, 27 Oct 2021 16:20:12 -0700 Subject: [PATCH 2/6] use basechart in test too --- .../alerts/incidentRules/triggersChart.spec.jsx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx b/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx index 6294ae35f8f6a8..a4fd629df2b235 100644 --- a/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx +++ b/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx @@ -2,7 +2,7 @@ import {mountWithTheme} from 'sentry-test/enzyme'; import {initializeOrg} from 'sentry-test/initializeOrg'; import {Client} from 'app/api'; -import AreaChart from 'app/components/charts/areaChart'; +import BaseChart from 'app/components/charts/baseChart'; import TriggersChart from 'app/views/alerts/incidentRules/triggers/chart'; jest.mock('app/components/charts/areaChart'); @@ -14,7 +14,7 @@ describe('Incident Rules Create', () => { beforeEach(() => { MockApiClient.clearMockResponses(); - AreaChart.default = jest.fn(() => null); + BaseChart.default = jest.fn(() => null); api = new Client(); eventStatsMock = MockApiClient.addMockResponse({ url: '/organizations/org-slug/events-stats/', @@ -73,7 +73,7 @@ describe('Incident Rules Create', () => { }) ); - expect(AreaChart).toHaveBeenCalledWith( + expect(BaseChart).toHaveBeenCalledWith( expect.objectContaining({ series: [{data: expect.objectContaining({length: 1}), seriesName: 'count()'}], }), @@ -137,15 +137,15 @@ describe('Incident Rules Create', () => { ); // "series" accessed directly to assist with jest diff - expect(AreaChart.mock.calls[0][0].series).toEqual([ + expect(BaseChart.mock.calls[0][0].series).toEqual([ {data: expect.anything(), seriesName: 'count()'}, {data: expect.anything(), seriesName: 'Minimum'}, {data: expect.anything(), seriesName: 'Average'}, {data: expect.anything(), seriesName: 'Maximum'}, ]); - expect(AreaChart.mock.calls[0][0].series[0].data).toHaveLength(1199); - expect(AreaChart.mock.calls[0][0].series[1].data).toHaveLength(239); - expect(AreaChart.mock.calls[0][0].series[2].data).toHaveLength(239); - expect(AreaChart.mock.calls[0][0].series[3].data).toHaveLength(239); + expect(BaseChart.mock.calls[0][0].series[0].data).toHaveLength(1199); + expect(BaseChart.mock.calls[0][0].series[1].data).toHaveLength(239); + expect(BaseChart.mock.calls[0][0].series[2].data).toHaveLength(239); + expect(BaseChart.mock.calls[0][0].series[3].data).toHaveLength(239); }); }); From a4d9417624ffcb93c831f8e96238f42d69b0d7fa Mon Sep 17 00:00:00 2001 From: Taylan Gocmen Date: Wed, 27 Oct 2021 16:38:38 -0700 Subject: [PATCH 3/6] more mock --- tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx b/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx index a4fd629df2b235..e2afe7da660914 100644 --- a/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx +++ b/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx @@ -5,7 +5,7 @@ import {Client} from 'app/api'; import BaseChart from 'app/components/charts/baseChart'; import TriggersChart from 'app/views/alerts/incidentRules/triggers/chart'; -jest.mock('app/components/charts/areaChart'); +jest.mock('app/components/charts/baseChart'); describe('Incident Rules Create', () => { let eventStatsMock; From 5002061e93370ce1e339afaed4123e8791cce55f Mon Sep 17 00:00:00 2001 From: Taylan Gocmen Date: Wed, 27 Oct 2021 17:45:38 -0700 Subject: [PATCH 4/6] revert back to area chart --- static/app/components/charts/areaChart.tsx | 46 ++++++++++--------- .../triggers/chart/thresholdsChart.tsx | 34 ++------------ .../incidentRules/triggersChart.spec.jsx | 18 ++++---- 3 files changed, 38 insertions(+), 60 deletions(-) diff --git a/static/app/components/charts/areaChart.tsx b/static/app/components/charts/areaChart.tsx index 3c67484fe52018..2aaa58c9cf753e 100644 --- a/static/app/components/charts/areaChart.tsx +++ b/static/app/components/charts/areaChart.tsx @@ -12,37 +12,41 @@ export type AreaChartSeries = Series & Omit & { series: AreaChartSeries[]; + additionalSeries?: EChartOption.SeriesLine[]; stacked?: boolean; }; class AreaChart extends React.Component { render() { - const {series, stacked, colors, ...props} = this.props; + const {series, additionalSeries, stacked, colors, ...props} = this.props; return ( - AreaSeries({ - stack: stacked ? 'area' : undefined, - name: seriesName, - data: data.map(({name, value}) => [name, value]), - lineStyle: { - color: colors?.[i], - opacity: 1, - width: 0.4, - }, - areaStyle: { - color: colors?.[i], - opacity: 1.0, - }, - animation: false, - animationThreshold: 1, - animationDuration: 0, - ...otherSeriesProps, - }) - )} + series={[ + ...series.map(({seriesName, data, ...otherSeriesProps}, i) => + AreaSeries({ + stack: stacked ? 'area' : undefined, + name: seriesName, + data: data.map(({name, value}) => [name, value]), + lineStyle: { + color: colors?.[i], + opacity: 1, + width: 0.4, + }, + areaStyle: { + color: colors?.[i], + opacity: 1.0, + }, + animation: false, + animationThreshold: 1, + animationDuration: 0, + ...otherSeriesProps, + }) + ), + ...(additionalSeries || []), + ]} /> ); } diff --git a/static/app/views/alerts/incidentRules/triggers/chart/thresholdsChart.tsx b/static/app/views/alerts/incidentRules/triggers/chart/thresholdsChart.tsx index d9481a51cafcfa..bb284eb5f22504 100644 --- a/static/app/views/alerts/incidentRules/triggers/chart/thresholdsChart.tsx +++ b/static/app/views/alerts/incidentRules/triggers/chart/thresholdsChart.tsx @@ -3,11 +3,9 @@ import color from 'color'; import debounce from 'lodash/debounce'; import flatten from 'lodash/flatten'; -import {AreaChartSeries} from 'app/components/charts/areaChart'; -import BaseChart from 'app/components/charts/baseChart'; +import AreaChart, {AreaChartSeries} from 'app/components/charts/areaChart'; import Graphic from 'app/components/charts/components/graphic'; import {LineChartSeries} from 'app/components/charts/lineChart'; -import AreaSeries from 'app/components/charts/series/areaSeries'; import LineSeries from 'app/components/charts/series/lineSeries'; import space from 'app/styles/space'; import {GlobalSelection} from 'app/types'; @@ -363,7 +361,7 @@ export default class ThresholdsChart extends PureComponent { }; return ( - { ]) ), })} - series={[ - ...dataWithoutRecentBucket.map( - ({seriesName, data: _data, ...otherSeriesProps}) => - AreaSeries({ - name: seriesName, - data: _data.map(({name, value}) => [name, value]), - lineStyle: { - opacity: 1, - width: 0.4, - }, - areaStyle: { - opacity: 1.0, - }, - animation: false, - animationThreshold: 1, - animationDuration: 0, - ...otherSeriesProps, - }) - ), + series={[...dataWithoutRecentBucket, ...comparisonMarkLines]} + additionalSeries={[ ...comparisonDataWithoutRecentBucket.map(({data: _data, ...otherSeriesProps}) => LineSeries({ name: comparisonSeriesName, @@ -410,13 +391,6 @@ export default class ThresholdsChart extends PureComponent { ...otherSeriesProps, }) ), - ...comparisonMarkLines.map(({seriesName, data: _data, ...seriesProps}) => - LineSeries({ - name: seriesName, - data: _data.map(({name, value}) => [name, value]), - ...seriesProps, - }) - ), ]} onFinished={() => { // We want to do this whenever the chart finishes re-rendering so that we can update the dimensions of diff --git a/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx b/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx index e2afe7da660914..6294ae35f8f6a8 100644 --- a/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx +++ b/tests/js/spec/views/alerts/incidentRules/triggersChart.spec.jsx @@ -2,10 +2,10 @@ import {mountWithTheme} from 'sentry-test/enzyme'; import {initializeOrg} from 'sentry-test/initializeOrg'; import {Client} from 'app/api'; -import BaseChart from 'app/components/charts/baseChart'; +import AreaChart from 'app/components/charts/areaChart'; import TriggersChart from 'app/views/alerts/incidentRules/triggers/chart'; -jest.mock('app/components/charts/baseChart'); +jest.mock('app/components/charts/areaChart'); describe('Incident Rules Create', () => { let eventStatsMock; @@ -14,7 +14,7 @@ describe('Incident Rules Create', () => { beforeEach(() => { MockApiClient.clearMockResponses(); - BaseChart.default = jest.fn(() => null); + AreaChart.default = jest.fn(() => null); api = new Client(); eventStatsMock = MockApiClient.addMockResponse({ url: '/organizations/org-slug/events-stats/', @@ -73,7 +73,7 @@ describe('Incident Rules Create', () => { }) ); - expect(BaseChart).toHaveBeenCalledWith( + expect(AreaChart).toHaveBeenCalledWith( expect.objectContaining({ series: [{data: expect.objectContaining({length: 1}), seriesName: 'count()'}], }), @@ -137,15 +137,15 @@ describe('Incident Rules Create', () => { ); // "series" accessed directly to assist with jest diff - expect(BaseChart.mock.calls[0][0].series).toEqual([ + expect(AreaChart.mock.calls[0][0].series).toEqual([ {data: expect.anything(), seriesName: 'count()'}, {data: expect.anything(), seriesName: 'Minimum'}, {data: expect.anything(), seriesName: 'Average'}, {data: expect.anything(), seriesName: 'Maximum'}, ]); - expect(BaseChart.mock.calls[0][0].series[0].data).toHaveLength(1199); - expect(BaseChart.mock.calls[0][0].series[1].data).toHaveLength(239); - expect(BaseChart.mock.calls[0][0].series[2].data).toHaveLength(239); - expect(BaseChart.mock.calls[0][0].series[3].data).toHaveLength(239); + expect(AreaChart.mock.calls[0][0].series[0].data).toHaveLength(1199); + expect(AreaChart.mock.calls[0][0].series[1].data).toHaveLength(239); + expect(AreaChart.mock.calls[0][0].series[2].data).toHaveLength(239); + expect(AreaChart.mock.calls[0][0].series[3].data).toHaveLength(239); }); }); From a106a9e2c05b5187c4928428dabfc26e5cabc968 Mon Sep 17 00:00:00 2001 From: Taylan Gocmen Date: Wed, 27 Oct 2021 18:06:22 -0700 Subject: [PATCH 5/6] fix series --- static/app/components/charts/areaChart.tsx | 58 ++++++++++------------ 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/static/app/components/charts/areaChart.tsx b/static/app/components/charts/areaChart.tsx index 2aaa58c9cf753e..d5fe863e8165cf 100644 --- a/static/app/components/charts/areaChart.tsx +++ b/static/app/components/charts/areaChart.tsx @@ -18,37 +18,33 @@ type Props = Omit & { class AreaChart extends React.Component { render() { - const {series, additionalSeries, stacked, colors, ...props} = this.props; - - return ( - - AreaSeries({ - stack: stacked ? 'area' : undefined, - name: seriesName, - data: data.map(({name, value}) => [name, value]), - lineStyle: { - color: colors?.[i], - opacity: 1, - width: 0.4, - }, - areaStyle: { - color: colors?.[i], - opacity: 1.0, - }, - animation: false, - animationThreshold: 1, - animationDuration: 0, - ...otherSeriesProps, - }) - ), - ...(additionalSeries || []), - ]} - /> - ); + const {series: _series, additionalSeries, stacked, colors, ...props} = this.props; + + const series = [ + ..._series.map(({seriesName, data, ...otherSeriesProps}, i) => + AreaSeries({ + stack: stacked ? 'area' : undefined, + name: seriesName, + data: data.map(({name, value}) => [name, value]), + lineStyle: { + color: colors?.[i], + opacity: 1, + width: 0.4, + }, + areaStyle: { + color: colors?.[i], + opacity: 1.0, + }, + animation: false, + animationThreshold: 1, + animationDuration: 0, + ...otherSeriesProps, + }) + ), + ...(additionalSeries || []), + ]; + + return ; } } From 4dbf219234b12235d8468cc193eaba4ad5d5d7a9 Mon Sep 17 00:00:00 2001 From: Taylan Gocmen Date: Wed, 27 Oct 2021 18:24:34 -0700 Subject: [PATCH 6/6] complete bypass area chart --- static/app/components/charts/areaChart.tsx | 56 +++++++++++----------- static/app/components/charts/baseChart.tsx | 10 +++- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/static/app/components/charts/areaChart.tsx b/static/app/components/charts/areaChart.tsx index d5fe863e8165cf..3c67484fe52018 100644 --- a/static/app/components/charts/areaChart.tsx +++ b/static/app/components/charts/areaChart.tsx @@ -12,39 +12,39 @@ export type AreaChartSeries = Series & Omit & { series: AreaChartSeries[]; - additionalSeries?: EChartOption.SeriesLine[]; stacked?: boolean; }; class AreaChart extends React.Component { render() { - const {series: _series, additionalSeries, stacked, colors, ...props} = this.props; - - const series = [ - ..._series.map(({seriesName, data, ...otherSeriesProps}, i) => - AreaSeries({ - stack: stacked ? 'area' : undefined, - name: seriesName, - data: data.map(({name, value}) => [name, value]), - lineStyle: { - color: colors?.[i], - opacity: 1, - width: 0.4, - }, - areaStyle: { - color: colors?.[i], - opacity: 1.0, - }, - animation: false, - animationThreshold: 1, - animationDuration: 0, - ...otherSeriesProps, - }) - ), - ...(additionalSeries || []), - ]; - - return ; + const {series, stacked, colors, ...props} = this.props; + + return ( + + AreaSeries({ + stack: stacked ? 'area' : undefined, + name: seriesName, + data: data.map(({name, value}) => [name, value]), + lineStyle: { + color: colors?.[i], + opacity: 1, + width: 0.4, + }, + areaStyle: { + color: colors?.[i], + opacity: 1.0, + }, + animation: false, + animationThreshold: 1, + animationDuration: 0, + ...otherSeriesProps, + }) + )} + /> + ); } } diff --git a/static/app/components/charts/baseChart.tsx b/static/app/components/charts/baseChart.tsx index ef20200ba069a9..014c422559e912 100644 --- a/static/app/components/charts/baseChart.tsx +++ b/static/app/components/charts/baseChart.tsx @@ -69,6 +69,11 @@ type Props = { * be an array of ECharts "Series" components. */ series?: EChartOption.Series[]; + /** + * Additional Chart Series + * This is to pass series to BaseChart bypassing the wrappers like LineChart, AreaChart etc. + */ + additionalSeries?: EChartOption.SeriesLine[]; /** * Array of color codes to use in charts. May also take a function which is * provided with the current theme @@ -284,6 +289,7 @@ function BaseChartUnwrapped({ options = {}, series = [], + additionalSeries = [], yAxis = {}, xAxis = {}, @@ -338,8 +344,8 @@ function BaseChartUnwrapped({ ) ?? []; const resolvedSeries = !previousPeriod - ? transformedSeries - : [...transformedSeries, ...transformedPreviousPeriod]; + ? [...transformedSeries, ...additionalSeries] + : [...transformedSeries, ...transformedPreviousPeriod, ...additionalSeries]; const defaultAxesProps = {theme};