Merge pull request #1805 from influxdata/fix/graph-fixes

Graph Fixes for Bar Graphs and Add Hash-based Colors
pull/10616/head
Andrew Watkins 2017-08-07 13:21:21 -07:00 committed by GitHub
commit a7d16c8514
3 changed files with 66 additions and 24 deletions

View File

@ -10,6 +10,9 @@
### UI Improvements ### UI Improvements
1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written 1. [#1796](https://github.com/influxdata/chronograf/pull/1796): Add spinner to indicate data is being written
1. [#1800](https://github.com/influxdata/chronograf/pull/1796): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay
1. [#1805](https://github.com/influxdata/chronograf/pull/1805): Fix bar graphs overlapping
1. [#1805](https://github.com/influxdata/chronograf/pull/1805): Add series names hashing so that graph colors should stay the same for the same series across charts
1. [#1800](https://github.com/influxdata/chronograf/pull/1800): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay 1. [#1800](https://github.com/influxdata/chronograf/pull/1800): Embiggen text area for line protocol manual entry in Data Explorer's Write Data overlay
1. [#1812](https://github.com/influxdata/chronograf/pull/1812): Improve error message when request for Status Page News Feed fails 1. [#1812](https://github.com/influxdata/chronograf/pull/1812): Improve error message when request for Status Page News Feed fails

View File

@ -11,6 +11,12 @@ import {LINE_COLORS, multiColumnBarPlotter} from 'src/shared/graphs/helpers'
import DygraphLegend from 'src/shared/components/DygraphLegend' import DygraphLegend from 'src/shared/components/DygraphLegend'
import {buildYLabel} from 'shared/presenters' import {buildYLabel} from 'shared/presenters'
const hasherino = (str, len) =>
str
.split('')
.map(char => char.charCodeAt(0))
.reduce((hash, code) => hash + code, 0) % len
export default class Dygraph extends Component { export default class Dygraph extends Component {
constructor(props) { constructor(props) {
super(props) super(props)
@ -80,21 +86,29 @@ export default class Dygraph extends Component {
const graphRef = this.graphRef const graphRef = this.graphRef
const legendRef = this.legendRef const legendRef = this.legendRef
let finalLineColors = overrideLineColors const finalLineColors = [...(overrideLineColors || LINE_COLORS)]
if (finalLineColors === null) { const hashColorDygraphSeries = {}
finalLineColors = LINE_COLORS const {length} = finalLineColors
for (const seriesName in dygraphSeries) {
const series = dygraphSeries[seriesName]
const hashIndex = hasherino(seriesName, length)
const color = finalLineColors[hashIndex]
hashColorDygraphSeries[seriesName] = {...series, color}
} }
const yAxis = _.get(axes, ['y', 'bounds'], [null, null]) const yAxis = _.get(axes, ['y', 'bounds'], [null, null])
const y2Axis = _.get(axes, ['y2', 'bounds'], undefined) const y2Axis = _.get(axes, ['y2', 'bounds'], undefined)
const defaultOptions = { const defaultOptions = {
plugins: [ plugins: isBarGraph
new Dygraphs.Plugins.Crosshair({ ? []
direction: 'vertical', : [
}), new Dygraphs.Plugins.Crosshair({
], direction: 'vertical',
}),
],
labelsSeparateLines: false, labelsSeparateLines: false,
labelsKMB: true, labelsKMB: true,
rightGap: 0, rightGap: 0,
@ -103,12 +117,11 @@ export default class Dygraph extends Component {
fillGraph: isGraphFilled, fillGraph: isGraphFilled,
axisLineWidth: 2, axisLineWidth: 2,
gridLineWidth: 1, gridLineWidth: 1,
highlightCircleSize: 3, highlightCircleSize: isBarGraph ? 0 : 3,
animatedZooms: true, animatedZooms: true,
hideOverlayOnMouseOut: false, hideOverlayOnMouseOut: false,
colors: finalLineColors, colors: finalLineColors,
series: dygraphSeries, series: hashColorDygraphSeries,
ylabel: this.getLabel('y'),
axes: { axes: {
y: { y: {
valueRange: getRange(timeSeries, yAxis, ruleValues), valueRange: getRange(timeSeries, yAxis, ruleValues),
@ -119,7 +132,7 @@ export default class Dygraph extends Component {
}, },
highlightSeriesOpts: { highlightSeriesOpts: {
strokeWidth: 2, strokeWidth: 2,
highlightCircleSize: 5, highlightCircleSize: isBarGraph ? 0 : 5,
}, },
legendFormatter: legend => { legendFormatter: legend => {
if (!legend.x) { if (!legend.x) {
@ -259,6 +272,7 @@ export default class Dygraph extends Component {
dygraphSeries, dygraphSeries,
ruleValues, ruleValues,
isBarGraph, isBarGraph,
overrideLineColors,
} = this.props } = this.props
const dygraph = this.dygraph const dygraph = this.dygraph
@ -272,6 +286,17 @@ export default class Dygraph extends Component {
const y2 = _.get(axes, ['y2', 'bounds'], undefined) const y2 = _.get(axes, ['y2', 'bounds'], undefined)
const timeSeries = this.getTimeSeries() const timeSeries = this.getTimeSeries()
const ylabel = this.getLabel('y') const ylabel = this.getLabel('y')
const finalLineColors = [...(overrideLineColors || LINE_COLORS)]
const hashColorDygraphSeries = {}
const {length} = finalLineColors
for (const seriesName in dygraphSeries) {
const series = dygraphSeries[seriesName]
const hashIndex = hasherino(seriesName, length)
const color = finalLineColors[hashIndex]
hashColorDygraphSeries[seriesName] = {...series, color}
}
const updateOptions = { const updateOptions = {
labels, labels,
@ -288,7 +313,8 @@ export default class Dygraph extends Component {
stepPlot: options.stepPlot, stepPlot: options.stepPlot,
stackedGraph: options.stackedGraph, stackedGraph: options.stackedGraph,
underlayCallback: options.underlayCallback, underlayCallback: options.underlayCallback,
series: dygraphSeries, colors: finalLineColors,
series: hashColorDygraphSeries,
plotter: isBarGraph ? multiColumnBarPlotter : null, plotter: isBarGraph ? multiColumnBarPlotter : null,
visibility: this.visibility(), visibility: this.visibility(),
} }

View File

@ -26,7 +26,7 @@ export const darkenColor = colorStr => {
return `rgb(${color.r},${color.g},${color.b})` return `rgb(${color.r},${color.g},${color.b})`
} }
// Bar Graph code below is from http://dygraphs.com/tests/plotters.html // Bar Graph code below is adapted from http://dygraphs.com/tests/plotters.html
export const multiColumnBarPlotter = e => { export const multiColumnBarPlotter = e => {
// We need to handle all the series simultaneously. // We need to handle all the series simultaneously.
if (e.seriesIndex !== 0) { if (e.seriesIndex !== 0) {
@ -51,24 +51,34 @@ export const multiColumnBarPlotter = e => {
} }
} }
const barWidth = Math.floor(2.0 / 3 * minSep) // calculate bar width using some graphics math while
// ensuring a bar is never smaller than one px, so it is always rendered
const barWidth = Math.max(Math.floor(2.0 / 3.0 * minSep), 1.0)
const fillColors = [] const fillColors = []
const strokeColors = g.getColors() const strokeColors = g.getColors()
let selPointX
if (g.selPoints_ && g.selPoints_.length) {
selPointX = g.selPoints_[0].canvasx
}
for (let i = 0; i < strokeColors.length; i++) { for (let i = 0; i < strokeColors.length; i++) {
fillColors.push(darkenColor(strokeColors[i])) fillColors.push(darkenColor(strokeColors[i]))
} }
ctx.lineWidth = 2
for (let j = 0; j < sets.length; j++) { for (let j = 0; j < sets.length; j++) {
ctx.fillStyle = fillColors[j]
ctx.strokeStyle = strokeColors[j] ctx.strokeStyle = strokeColors[j]
for (let i = 0; i < sets[j].length; i++) { for (let i = 0; i < sets[j].length; i++) {
const p = sets[j][i] const p = sets[j][i]
const centerX = p.canvasx const centerX = p.canvasx
ctx.fillStyle = fillColors[j]
const xLeft = const xLeft =
sets.length === 1 sets.length === 1
? centerX - barWidth / 2 ? centerX - barWidth
: centerX - barWidth / 2 * (1 - j / (sets.length - 1)) : centerX - barWidth * (1 - j / sets.length)
ctx.fillRect( ctx.fillRect(
xLeft, xLeft,
@ -77,12 +87,15 @@ export const multiColumnBarPlotter = e => {
yBottom - p.canvasy yBottom - p.canvasy
) )
ctx.strokeRect( // hover highlighting
xLeft, if (selPointX === centerX) {
p.canvasy, ctx.strokeRect(
barWidth / sets.length, xLeft,
yBottom - p.canvasy p.canvasy,
) barWidth / sets.length,
yBottom - p.canvasy
)
}
} }
} }
} }