Default to zero for gauges
parent
2e8b4cb6b2
commit
97109e29e0
|
@ -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
|
|
@ -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
|
|
@ -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'],
|
|
@ -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)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
|
@ -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: [''],
|
||||
})
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue