Merge pull request #2055 from influxdata/chore/refactor-line-graph

REFACTOR:  LineGraph, SingleStat, and LineGraph+SingleStat
pull/10616/head
Andrew Watkins 2017-10-02 12:50:19 -07:00 committed by GitHub
commit f1fc9640d8
3 changed files with 136 additions and 155 deletions

View File

@ -1,67 +1,20 @@
import React, {PropTypes} from 'react'
import React, {PropTypes, Component} from 'react'
import Dygraph from 'shared/components/Dygraph'
import classnames from 'classnames'
import shallowCompare from 'react-addons-shallow-compare'
import SingleStat from 'src/shared/components/SingleStat'
import timeSeriesToDygraph from 'utils/timeSeriesToDygraph'
import lastValues from 'shared/parsing/lastValues'
const {array, arrayOf, bool, func, number, shape, string} = PropTypes
import {SINGLE_STAT_LINE_COLORS} from 'src/shared/graphs/helpers'
const SMALL_CELL_HEIGHT = 1
export default React.createClass({
displayName: 'LineGraph',
propTypes: {
data: arrayOf(shape({}).isRequired).isRequired,
axes: shape({
y: shape({
bounds: array,
label: string,
}),
y2: shape({
bounds: array,
label: string,
}),
}),
title: string,
isFetchingInitially: bool,
isRefreshing: bool,
underlayCallback: func,
isGraphFilled: bool,
isBarGraph: bool,
overrideLineColors: array,
queries: arrayOf(shape({}).isRequired).isRequired,
showSingleStat: bool,
displayOptions: shape({
stepPlot: bool,
stackedGraph: bool,
}),
activeQueryIndex: number,
ruleValues: shape({}),
timeRange: shape({
lower: string.isRequired,
}),
isInDataExplorer: bool,
synchronizer: func,
setResolution: func,
cellHeight: number,
cell: shape({}),
onZoom: func,
resizeCoords: shape(),
},
getDefaultProps() {
return {
underlayCallback: () => {},
isGraphFilled: true,
overrideLineColors: null,
}
},
class LineGraph extends Component {
constructor(props) {
super(props)
}
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState)
},
}
componentWillMount() {
const {data, activeQueryIndex, isInDataExplorer} = this.props
@ -70,7 +23,7 @@ export default React.createClass({
activeQueryIndex,
isInDataExplorer
)
},
}
componentWillUpdate(nextProps) {
const {data, activeQueryIndex} = this.props
@ -84,132 +37,144 @@ export default React.createClass({
nextProps.isInDataExplorer
)
}
},
}
render() {
const {
data,
axes,
cell,
isFetchingInitially,
isRefreshing,
isGraphFilled,
isBarGraph,
overrideLineColors,
title,
onZoom,
queries,
underlayCallback,
showSingleStat,
displayOptions,
ruleValues,
synchronizer,
timeRange,
cellHeight,
onZoom,
ruleValues,
isBarGraph,
resizeCoords,
synchronizer,
isRefreshing,
isGraphFilled,
showSingleStat,
displayOptions,
underlayCallback,
overrideLineColors,
isFetchingInitially,
} = this.props
const {labels, timeSeries, dygraphSeries} = this._timeSeries
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
if (isFetchingInitially) {
return (
<div className="graph-fetching">
<div className="graph-spinner" />
</div>
)
return <GraphSpinner />
}
const options = {
labels,
connectSeparatedPoints: true,
labelsKMB: true,
axisLineColor: '#383846',
gridLineColor: '#383846',
...displayOptions,
title,
labels,
rightGap: 0,
yRangePad: 10,
labelsKMB: true,
underlayCallback,
axisLabelWidth: 60,
drawAxesAtZero: true,
underlayCallback,
...displayOptions,
}
const singleStatOptions = {
...options,
highlightSeriesOpts: {
strokeWidth: 1.5,
},
}
const singleStatLineColors = [
'#7A65F2',
'#FFD255',
'#7CE490',
'#F95F53',
'#4591ED',
'#B1B6FF',
'#FFF6B8',
'#C6FFD0',
'#6BDFFF',
]
let roundedValue
if (showSingleStat) {
const lastValue = lastValues(data)[1]
const precision = 100.0
roundedValue = Math.round(+lastValue * precision) / precision
axisLineColor: '#383846',
gridLineColor: '#383846',
connectSeparatedPoints: true,
}
const lineColors = showSingleStat
? singleStatLineColors
? SINGLE_STAT_LINE_COLORS
: overrideLineColors
return (
<div className="dygraph graph--hasYLabel" style={{height: '100%'}}>
{isRefreshing ? this.renderSpinner() : null}
{isRefreshing ? <GraphLoadingDots /> : null}
<Dygraph
cell={cell}
resizeCoords={resizeCoords}
axes={axes}
onZoom={onZoom}
labels={labels}
queries={queries}
containerStyle={{width: '100%', height: '100%'}}
overrideLineColors={lineColors}
isGraphFilled={showSingleStat ? false : isGraphFilled}
timeRange={timeRange}
isBarGraph={isBarGraph}
timeSeries={timeSeries}
labels={labels}
options={showSingleStat ? singleStatOptions : options}
dygraphSeries={dygraphSeries}
ruleValues={ruleValues}
synchronizer={synchronizer}
timeRange={timeRange}
resizeCoords={resizeCoords}
overrideLineColors={lineColors}
dygraphSeries={dygraphSeries}
setResolution={this.props.setResolution}
onZoom={onZoom}
containerStyle={{width: '100%', height: '100%'}}
isGraphFilled={showSingleStat ? false : isGraphFilled}
options={options}
/>
{showSingleStat
? <div className="single-stat single-stat-line">
<span
className={classnames('single-stat--value', {
'single-stat--small': cellHeight === SMALL_CELL_HEIGHT,
})}
>
<span className="single-stat--shadow">
{roundedValue}
</span>
</span>
</div>
? <SingleStat data={data} cellHeight={cellHeight} />
: null}
</div>
)
},
}
}
renderSpinner() {
return (
<div className="graph-panel__refreshing">
<div />
<div />
<div />
</div>
)
},
})
const GraphLoadingDots = () =>
<div className="graph-panel__refreshing">
<div />
<div />
<div />
</div>
const GraphSpinner = () =>
<div className="graph-fetching">
<div className="graph-spinner" />
</div>
const {array, arrayOf, bool, func, number, shape, string} = PropTypes
LineGraph.defaultProps = {
underlayCallback: () => {},
isGraphFilled: true,
overrideLineColors: null,
}
LineGraph.propTypes = {
axes: shape({
y: shape({
bounds: array,
label: string,
}),
y2: shape({
bounds: array,
label: string,
}),
}),
title: string,
isFetchingInitially: bool,
isRefreshing: bool,
underlayCallback: func,
isGraphFilled: bool,
isBarGraph: bool,
overrideLineColors: array,
showSingleStat: bool,
displayOptions: shape({
stepPlot: bool,
stackedGraph: bool,
}),
activeQueryIndex: number,
ruleValues: shape({}),
timeRange: shape({
lower: string.isRequired,
}),
isInDataExplorer: bool,
synchronizer: func,
setResolution: func,
cellHeight: number,
cell: shape(),
onZoom: func,
resizeCoords: shape(),
queries: arrayOf(shape({}).isRequired).isRequired,
data: arrayOf(shape({}).isRequired).isRequired,
}
export default LineGraph

View File

@ -1,28 +1,20 @@
import React, {PropTypes} from 'react'
import React, {PropTypes, Component} from 'react'
import classnames from 'classnames'
import shallowCompare from 'react-addons-shallow-compare'
import lastValues from 'shared/parsing/lastValues'
const SMALL_CELL_HEIGHT = 1
export default React.createClass({
displayName: 'LineGraph',
propTypes: {
data: PropTypes.arrayOf(PropTypes.shape({})).isRequired,
title: PropTypes.string,
isFetchingInitially: PropTypes.bool,
cellHeight: PropTypes.number,
},
import {SMALL_CELL_HEIGHT} from 'src/shared/graphs/helpers'
class SingleStat extends Component {
shouldComponentUpdate(nextProps, nextState) {
return shallowCompare(this, nextProps, nextState)
},
}
render() {
const {data, cellHeight} = this.props
const {data, cellHeight, isFetchingInitially} = this.props
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
if (this.props.isFetchingInitially) {
if (isFetchingInitially) {
return (
<div className="graph-empty">
<h3 className="graph-spinner" />
@ -46,5 +38,15 @@ export default React.createClass({
</span>
</div>
)
},
})
}
}
const {arrayOf, bool, number, shape} = PropTypes
SingleStat.propTypes = {
data: arrayOf(shape()).isRequired,
isFetchingInitially: bool,
cellHeight: number,
}
export default SingleStat

View File

@ -17,6 +17,20 @@ export const LINE_COLORS = [
'#a0725b',
]
export const SMALL_CELL_HEIGHT = 1
export const SINGLE_STAT_LINE_COLORS = [
'#7A65F2',
'#FFD255',
'#7CE490',
'#F95F53',
'#4591ED',
'#B1B6FF',
'#FFF6B8',
'#C6FFD0',
'#6BDFFF',
]
export const darkenColor = colorStr => {
// Defined in dygraph-utils.js
const color = Dygraphs.toRGB_(colorStr)