Default to zero for gauges

pull/10616/head
Brandon Farmer 2018-04-11 12:43:41 -07:00
parent 2e8b4cb6b2
commit 97109e29e0
5 changed files with 179 additions and 93 deletions

View File

@ -1,92 +0,0 @@
import React, {PureComponent} from 'react'
import PropTypes from 'prop-types'
import lastValues from 'shared/parsing/lastValues'
import Gauge from 'shared/components/Gauge'
import {DEFAULT_GAUGE_COLORS} from 'src/shared/constants/thresholds'
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'src/shared/constants'
import {colorsStringSchema} from 'shared/schemas'
class GaugeChart extends PureComponent {
render() {
const {
data,
cellID,
cellHeight,
isFetchingInitially,
colors,
resizeCoords,
resizerTopHeight,
prefix,
suffix,
} = this.props
// If data for this graph is being fetched for the first time, show a graph-wide spinner.
if (isFetchingInitially) {
return (
<div className="graph-empty">
<h3 className="graph-spinner" />
</div>
)
}
const lastValue = lastValues(data)[1]
const precision = 100.0
const roundedValue = Math.round(+lastValue * precision) / precision
// When a new height is passed the Gauge component resizes internally
// Passing in a new often ensures the gauge appears sharp
const thisGaugeIsResizing = resizeCoords ? cellID === resizeCoords.i : false
const initialCellHeight =
cellHeight && (cellHeight * DASHBOARD_LAYOUT_ROW_HEIGHT).toString()
const resizeCoordsHeight =
resizeCoords &&
thisGaugeIsResizing &&
(resizeCoords.h * DASHBOARD_LAYOUT_ROW_HEIGHT).toString()
const height = (
resizeCoordsHeight ||
initialCellHeight ||
resizerTopHeight ||
300
).toString()
return (
<div className="single-stat">
<Gauge
width="900"
height={height}
colors={colors}
gaugePosition={roundedValue}
prefix={prefix}
suffix={suffix}
/>
</div>
)
}
}
const {arrayOf, bool, number, shape, string} = PropTypes
GaugeChart.defaultProps = {
colors: stringifyColorValues(DEFAULT_GAUGE_COLORS),
}
GaugeChart.propTypes = {
data: arrayOf(shape()).isRequired,
isFetchingInitially: bool,
cellID: string,
cellHeight: number,
resizerTopHeight: number,
resizeCoords: shape(),
colors: colorsStringSchema,
prefix: string.isRequired,
suffix: string.isRequired,
}
export default GaugeChart

View File

@ -0,0 +1,112 @@
import React, {PureComponent, ReactNode} from 'react'
import getLastValues, {TimeSeriesResponse} from 'src/shared/parsing/lastValues'
import Gauge from 'src/shared/components/Gauge'
import _ from 'lodash'
import {DEFAULT_GAUGE_COLORS} from 'src/shared/constants/thresholds'
import {stringifyColorValues} from 'src/shared/constants/colorOperations'
import {DASHBOARD_LAYOUT_ROW_HEIGHT} from 'src/shared/constants'
interface Color {
type: string
hex: string
id: string
name: string
value: string
}
interface Props {
data: TimeSeriesResponse[]
isFetchingInitially: boolean
cellID: string
cellHeight?: number
colors?: Color[]
prefix: string
suffix: string
resizerTopHeight?: number
resizeCoords?: {
i: string
h: number
}
}
class GaugeChart extends PureComponent<Props> {
public static defaultProps: Partial<Props> = {
colors: stringifyColorValues(DEFAULT_GAUGE_COLORS),
}
public render() {
const {isFetchingInitially, colors, prefix, suffix} = this.props
if (isFetchingInitially) {
return this.spinner
}
return (
<div className="single-stat">
<Gauge
width="900"
height={this.height}
colors={colors}
gaugePosition={this.lastValue}
prefix={prefix}
suffix={suffix}
/>
</div>
)
}
private get height(): string {
const {resizerTopHeight} = this.props
return (
this.resizeCoordsHeight ||
this.initialCellHeight ||
resizerTopHeight ||
300
).toString()
}
private get resizeCoordsHeight(): string {
const {resizeCoords} = this.props
if (resizeCoords && this.isResizing) {
return (resizeCoords.h * DASHBOARD_LAYOUT_ROW_HEIGHT).toString()
}
return null
}
private get initialCellHeight(): string {
const {cellHeight} = this.props
if (cellHeight) {
return (cellHeight * DASHBOARD_LAYOUT_ROW_HEIGHT).toString()
}
return null
}
private get isResizing(): boolean {
const {resizeCoords, cellID} = this.props
return resizeCoords ? cellID === resizeCoords.i : false
}
private get lastValue(): number {
const {data} = this.props
const {lastValues} = getLastValues(data)
const precision = 100.0
return Math.round(_.get(lastValues, 1, 0) * precision) / precision
}
private get spinner(): ReactNode {
return (
<div className="graph-empty">
<h3 className="graph-spinner" />
</div>
)
}
}
export default GaugeChart

View File

@ -1,6 +1,28 @@
import _ from 'lodash'
export default function(timeSeriesResponse) {
interface Result {
lastValues: any[]
series: any[]
}
interface Series {
value: any
column: string
}
interface TimeSeriesResult {
series: Series[]
}
export interface TimeSeriesResponse {
response: {
result: TimeSeriesResult[]
}
}
export default function(
timeSeriesResponse: TimeSeriesResponse[] | null
): Result {
const values = _.get(
timeSeriesResponse,
['0', 'response', 'results', '0', 'series', '0', 'values'],

View File

@ -0,0 +1,34 @@
import {shallow} from 'enzyme'
import React from 'react'
import Gauge from 'src/shared/components/Gauge'
import GaugeChart from 'src/shared/components/GaugeChart'
const defaultProps = {
data: [],
isFetchingInitially: false,
cellID: '',
prefix: '',
suffix: '',
}
const setup = (overrides = {}) => {
const props = {
...defaultProps,
...overrides,
}
return shallow(<GaugeChart {...props} />)
}
describe('GaugeChart', () => {
describe('render', () => {
describe('when data is empty', () => {
it('renders the correct number', () => {
const wrapper = setup()
expect(wrapper.find(Gauge).exists()).toBe(true)
expect(wrapper.find(Gauge).props().gaugePosition).toBe(0)
})
})
})
})

View File

@ -0,0 +1,10 @@
import lastValues from 'src/shared/parsing/lastValues'
describe('lastValues', () => {
it('returns the correct value when response is empty', () => {
expect(lastValues([])).toEqual({
lastValues: [''],
series: [''],
})
})
})