Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 19 additions & 47 deletions src/sentry/static/sentry/app/components/charts/areaChart.jsx
Original file line number Diff line number Diff line change
@@ -1,68 +1,40 @@
import moment from 'moment';
import React from 'react';
import PropTypes from 'prop-types';

import theme from 'app/utils/theme';
import SentryTypes from 'app/sentryTypes';

import AreaSeries from './series/areaSeries';
import BaseChart from './baseChart';
import LineSeries from './series/lineSeries';
import XAxis from './components/xAxis';
import YAxis from './components/yAxis';

class AreaChart extends React.Component {
static propTypes = {
...BaseChart.propTypes,

/**
* Display previous period as a line
*/
previousPeriod: SentryTypes.SeriesUnit,
stacked: PropTypes.bool,
};

render() {
const {series, previousPeriod, ...props} = this.props;
if (!series.length) return null;
const {series, stacked, ...props} = this.props;
const colors =
(series && series.length && theme.charts.getColorPalette(series.length)) || {};

return (
<BaseChart
{...props}
options={{
xAxis: XAxis({
type: 'time',
boundaryGap: false,
axisLabel: {
formatter: (value, index) => moment(value).format('MMM D'),
series={series.map((s, i) =>
AreaSeries({
stack: stacked ? 'area' : false,
name: s.seriesName,
data: s.data.map(({name, value}) => [name, value]),
lineStyle: {
color: '#fff',
width: 2,
},
areaStyle: {
color: colors[i],
opacity: 1.0,
},
}),
yAxis: YAxis({}),
series: [
...series.map((s, i) =>
AreaSeries({
stack: 'test',
name: s.seriesName,
data: s.data.map(({name, value}) => [name, value]),
lineStyle: {
color: '#fff',
width: 2,
},
areaStyle: {
color: theme.charts.colors[i],
opacity: 1.0,
},
})
),
previousPeriod &&
LineSeries({
name: previousPeriod.seriesName,
data: previousPeriod.data.map(({name, value}) => [name, value]),
lineStyle: {
color: theme.gray1,
type: 'dotted',
},
}),
],
}}
})
)}
/>
);
}
Expand Down
28 changes: 10 additions & 18 deletions src/sentry/static/sentry/app/components/charts/barChart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,26 @@ import React from 'react';

import BarSeries from './series/barSeries.jsx';
import BaseChart from './baseChart';
import YAxis from './components/yAxis';
import XAxis from './components/xAxis';

export default class BarChart extends React.Component {
static propTypes = {
...BaseChart.propTypes,
};

render() {
const {series, stacked} = this.props;
const {series, stacked, xAxis, ...props} = this.props;

return (
<BaseChart
{...this.props}
options={{
xAxis: XAxis({
type: 'category',
}),
yAxis: YAxis({}),
series: series.map((s, i) => {
return BarSeries({
name: s.seriesName,
stack: stacked ? 'stack1' : null,
data: s.data.map(({value, name}) => [name, value]),
});
}),
...this.props.options,
}}
{...props}
xAxis={xAxis !== null ? {...(xAxis || {}), boundaryGap: true} : null}
series={series.map((s, i) => {
return BarSeries({
name: s.seriesName,
stack: stacked ? 'stack1' : null,
data: s.data.map(({value, name}) => [name, value]),
});
})}
/>
);
}
Expand Down
93 changes: 78 additions & 15 deletions src/sentry/static/sentry/app/components/charts/baseChart.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ import React from 'react';
import ReactEchartsCore from 'echarts-for-react/lib/core';
import echarts from 'echarts/lib/echarts';

import SentryTypes from 'app/sentryTypes';
import theme from 'app/utils/theme';

import Grid from './components/grid';
import LineSeries from './series/lineSeries';
import Tooltip from './components/tooltip';
import YAxis from './components/yAxis';
import XAxis from './components/xAxis';

// If dimension is a number conver it to pixels, otherwise use dimension without transform
const getDimensionValue = dimension => {
Expand All @@ -23,12 +27,29 @@ class BaseChart extends React.Component {
static propTypes = {
// TODO: Pull out props from generic `options` object
// so that we can better document them in prop types
// e.g:
// series: SentryTypes.Series,

// see: https://ecomfe.github.io/echarts-doc/public/en/option.html
options: PropTypes.object,

// Chart Series
// This is different than the interface to higher level charts, these need to be
// an array of ECharts "Series" components.
series: SentryTypes.EChartsSeries,

// Array of color codes to use in charts
colors: PropTypes.arrayOf(PropTypes.string),

// Must be explicitly `null` to disable xAxis
xAxis: SentryTypes.EChartsXAxis,

// Must be explicitly `null` to disable yAxis
yAxis: SentryTypes.EChartsYAxis,

// Tooltip options
tooltip: SentryTypes.EChartsTooltip,

// ECharts Grid options
grid: SentryTypes.EChartsGrid,

// Chart height
height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),

Expand All @@ -48,9 +69,6 @@ class BaseChart extends React.Component {
// example theme: https://github.com/apache/incubator-echarts/blob/master/theme/dark.js
theme: PropTypes.string,

// Default array of color codes to use in charts
colors: PropTypes.arrayOf(PropTypes.string),

// states whether or not to merge with previous `option`
notMerge: PropTypes.bool,

Expand All @@ -68,6 +86,16 @@ class BaseChart extends React.Component {

// Forwarded Ref
forwardedRef: PropTypes.object,

// Custom chart props that are implemented by us (and not a feature of eCharts)
/**
* Display previous period as a LineSeries
*/
previousPeriod: SentryTypes.SeriesUnit,

// If data is grouped by date, then apply default date formatting to
// x-axis and tooltips.
isGroupedByDate: PropTypes.bool,
};

static defaultProps = {
Expand All @@ -76,8 +104,13 @@ class BaseChart extends React.Component {
renderer: 'svg',
notMerge: true,
lazyUpdate: false,
options: {},
onChartReady: () => {},
options: {},

series: [],
xAxis: {},
yAxis: {},
isGroupedByDate: false,
};

handleChartReady = (...args) => {
Expand All @@ -86,7 +119,7 @@ class BaseChart extends React.Component {
};

getColorPalette = () => {
let {series} = this.props.options;
let {series} = this.props;

return series && series.length
? theme.charts.getColorPalette(series.length)
Expand All @@ -95,12 +128,21 @@ class BaseChart extends React.Component {

render() {
let {
options,
colors,
grid,
tooltip,
series,
yAxis,
xAxis,

isGroupedByDate,
previousPeriod,

devicePixelRatio,
height,
width,
renderer,
options,
notMerge,
lazyUpdate,
silent,
Expand All @@ -113,12 +155,6 @@ class BaseChart extends React.Component {
<ReactEchartsCore
ref={forwardedRef}
echarts={echarts}
option={{
color: colors || this.getColorPalette(),
grid: Grid(),
tooltip: Tooltip(),
...options,
}}
notMerge={notMerge}
lazyUpdate={lazyUpdate}
silent={silent}
Expand All @@ -136,6 +172,33 @@ class BaseChart extends React.Component {
width: getDimensionValue(width),
...style,
}}
option={{
...options,
color: colors || this.getColorPalette(),
grid: Grid(grid),
tooltip: tooltip !== null ? Tooltip({isGroupedByDate, ...tooltip}) : null,
yAxis: yAxis !== null ? YAxis(yAxis) : null,
xAxis:
xAxis !== null
? XAxis({
...xAxis,
isGroupedByDate,
})
: null,
series: !previousPeriod
? series
: [
...series,
LineSeries({
name: previousPeriod.seriesName,
data: previousPeriod.data.map(({name, value}) => [name, value]),
lineStyle: {
color: theme.gray1,
type: 'dotted',
},
}),
],
}}
/>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ export default function Grid(props = {}) {
left: '10%',

right: '10%',

...props,
};
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,69 @@
import moment from 'moment';
import 'echarts/lib/component/tooltip';

export default function Tooltip(props = {}) {
const DEFAULT_TRUNCATE_LENGTH = 80;

// Truncates labels for tooltip
function truncateLabel(seriesName, truncate) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These functions are moved from app/organizationDiscover/result/utils

if (!truncate) {
return seriesName;
}

let result = seriesName;
let truncateLength = typeof truncate === 'number' ? truncate : DEFAULT_TRUNCATE_LENGTH;
0;

if (seriesName.length > truncateLength) {
result = seriesName.substring(0, truncateLength) + '…';
}
return result;
}

function formatAxisLabel(value, isTimestamp) {
if (!isTimestamp) {
return value;
}

return moment(value).format('MMM D, YYYY');
}

function getFormatter({filter, isGroupedByDate, truncate}) {
const getFilter = seriesParam => {
const value = seriesParam.data[1];
if (typeof filter === 'function') {
return filter(value);
}

return true;
};

return seriesParams => {
const label =
seriesParams.length &&
formatAxisLabel(seriesParams[0].axisValueLabel, isGroupedByDate);
return [
`<div>${truncateLabel(label, truncate)}</div>`,
seriesParams
.filter(getFilter)
.map(
s =>
`<div>${s.marker} ${truncateLabel(s.seriesName, truncate)}: ${s
.data[1]}</div>`
)
.join(''),
].join('');
};
}

export default function Tooltip(
{filter, isGroupedByDate, formatter, truncate, ...props} = {}
) {
formatter = formatter || getFormatter({filter, isGroupedByDate, truncate});

return {
show: true,
trigger: 'axis',
formatter,
...props,
};
}
Loading