diff --git a/ui/.eslintrc b/ui/.eslintrc index e5a27f5133..360af5a034 100644 --- a/ui/.eslintrc +++ b/ui/.eslintrc @@ -110,7 +110,6 @@ 'no-new': 2, 'no-octal-escape': 2, 'no-octal': 2, - 'no-param-reassign': 2, 'no-proto': 2, 'no-redeclare': 2, 'no-script-url': 2, diff --git a/ui/src/kapacitor/components/RuleGraph.js b/ui/src/kapacitor/components/RuleGraph.js index 4a27b8f0fd..d4b7e87542 100644 --- a/ui/src/kapacitor/components/RuleGraph.js +++ b/ui/src/kapacitor/components/RuleGraph.js @@ -4,7 +4,6 @@ import AutoRefresh from 'shared/components/AutoRefresh'; import LineGraph from 'shared/components/LineGraph'; const RefreshingLineGraph = AutoRefresh(LineGraph); -const OUT_OF_RANGE = "out of range"; export const RuleGraph = React.createClass({ propTypes: { source: PropTypes.shape({ @@ -40,32 +39,14 @@ export const RuleGraph = React.createClass({ ); } - const {value, rangeValue, operator} = rule.values; - const tenPercent = 0.1; - let lower, upper; - let upperRange, lowerRange; - - if (value !== "" && operator === OUT_OF_RANGE) { - lower = Math.min(+rule.values.rangeValue, +rule.values.value); - lowerRange = lower ? lower + lower * tenPercent : null; - } - - if (rangeValue !== "" && operator === OUT_OF_RANGE) { - upper = Math.max(+rule.values.rangeValue, +rule.values.value); - upperRange = upper ? upper + upper * tenPercent : null; - } - - // if either value is null, Dygraph will choose a sensible default - const range = {y: [lowerRange, upperRange]}; - return ( ); }, diff --git a/ui/src/shared/components/Dygraph.js b/ui/src/shared/components/Dygraph.js index dead72b1cf..1496d2abd3 100644 --- a/ui/src/shared/components/Dygraph.js +++ b/ui/src/shared/components/Dygraph.js @@ -2,7 +2,13 @@ import React, {PropTypes} from 'react'; import Dygraph from '../../external/dygraph'; -const {arrayOf, object, array, number, bool, shape} = PropTypes; +const { + array, + arrayOf, + number, + bool, + shape, +} = PropTypes; const LINE_COLORS = [ '#00C9FF', @@ -28,13 +34,14 @@ export default React.createClass({ y: arrayOf(number), y2: arrayOf(number), }), - timeSeries: array.isRequired, // eslint-disable-line react/forbid-prop-types - labels: array.isRequired, // eslint-disable-line react/forbid-prop-types - options: object, // eslint-disable-line react/forbid-prop-types - containerStyle: object, // eslint-disable-line react/forbid-prop-types + timeSeries: array.isRequired, + labels: array.isRequired, + options: shape({}), + containerStyle: shape({}), isGraphFilled: bool, overrideLineColors: array, dygraphSeries: shape({}).isRequired, + ruleValues: shape({}), }, getDefaultProps() { @@ -54,7 +61,7 @@ export default React.createClass({ componentDidMount() { const timeSeries = this.getTimeSeries(); // dygraphSeries is a legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'}; - const {ranges, dygraphSeries} = this.props; + const {ranges, dygraphSeries, ruleValues} = this.props; const refs = this.refs; const graphContainerNode = refs.graphContainer; @@ -81,7 +88,7 @@ export default React.createClass({ series: dygraphSeries, axes: { y: { - valueRange: getRange(timeSeries, ranges.y), + valueRange: getRange(timeSeries, ranges.y, ruleValues.value, ruleValues.rangeValue), }, y2: { valueRange: getRange(timeSeries, ranges.y2), @@ -141,14 +148,14 @@ export default React.createClass({ } const timeSeries = this.getTimeSeries(); - const {labels, ranges, options, dygraphSeries} = this.props; + const {labels, ranges, options, dygraphSeries, ruleValues} = this.props; dygraph.updateOptions({ labels, file: timeSeries, axes: { y: { - valueRange: getRange(timeSeries, ranges.y), + valueRange: getRange(timeSeries, ranges.y, ruleValues.value, ruleValues.rangeValue), }, y2: { valueRange: getRange(timeSeries, ranges.y2), @@ -172,15 +179,28 @@ export default React.createClass({ }, }); -function getRange(timeSeries, override) { +const TEN_PERCENT = 0.1; + +function getRange(timeSeries, override, value = null, rangeValue = null) { if (override) { return override; } - let max = null; - let min = null; + const addPadding = (val) => { + if (val === null || val === '') { + return null; + } - timeSeries.forEach((series) => { + if (val < 0) { + return val - val * TEN_PERCENT; + } + + return val + val * TEN_PERCENT; + }; + + const points = [...timeSeries, [null, addPadding(value)], [null, addPadding(rangeValue)]]; + + const range = points.reduce(([min, max], series) => { for (let i = 1; i < series.length; i++) { const val = series[i]; @@ -196,13 +216,15 @@ function getRange(timeSeries, override) { min = Math.min(min, val); max = Math.max(max, val); } + + return [min, max]; } - }); + }, [null, null]); // Dygraph will not reliably plot X / Y axis labels if min and max are both 0 - if (min === 0 && max === 0) { + if (range[0] === 0 && range[1] === 0) { return [null, null]; } - return [min, max]; + return range; } diff --git a/ui/src/shared/components/LineGraph.js b/ui/src/shared/components/LineGraph.js index bd0af12420..85e9ff9aec 100644 --- a/ui/src/shared/components/LineGraph.js +++ b/ui/src/shared/components/LineGraph.js @@ -7,23 +7,34 @@ import _ from 'lodash'; import timeSeriesToDygraph from 'utils/timeSeriesToDygraph'; import lastValues from 'src/shared/parsing/lastValues'; +const { + array, + arrayOf, + number, + bool, + shape, + string, + func, +} = PropTypes; + export default React.createClass({ displayName: 'LineGraph', propTypes: { - data: PropTypes.arrayOf(PropTypes.shape({}).isRequired).isRequired, - ranges: PropTypes.shape({ - y: PropTypes.arrayOf(PropTypes.number), - y2: PropTypes.arrayOf(PropTypes.number), + data: arrayOf(shape({}).isRequired).isRequired, + ranges: shape({ + y: arrayOf(number), + y2: arrayOf(number), }), - title: PropTypes.string, - isFetchingInitially: PropTypes.bool, - isRefreshing: PropTypes.bool, - underlayCallback: PropTypes.func, - isGraphFilled: PropTypes.bool, - overrideLineColors: PropTypes.array, - queries: PropTypes.arrayOf(PropTypes.shape({}).isRequired).isRequired, - showSingleStat: PropTypes.bool, - activeQueryIndex: PropTypes.number, + title: string, + isFetchingInitially: bool, + isRefreshing: bool, + underlayCallback: func, + isGraphFilled: bool, + overrideLineColors: array, + queries: arrayOf(shape({}).isRequired).isRequired, + showSingleStat: bool, + activeQueryIndex: number, + ruleValues: shape({}), }, getDefaultProps() { @@ -50,7 +61,7 @@ export default React.createClass({ }, render() { - const {data, ranges, isFetchingInitially, isRefreshing, isGraphFilled, overrideLineColors, title, underlayCallback, queries, showSingleStat} = this.props; + const {data, ranges, isFetchingInitially, isRefreshing, isGraphFilled, overrideLineColors, title, underlayCallback, queries, showSingleStat, ruleValues} = 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. @@ -99,6 +110,7 @@ export default React.createClass({ options={options} dygraphSeries={dygraphSeries} ranges={ranges || this.getRanges()} + ruleValues={ruleValues} /> {showSingleStat ?
{roundedValue}
: null}