WIP encorporate new shape for yRanges => axes

pull/1797/head
Andrew Watkins 2017-07-24 15:01:23 -07:00
parent 38b7d17df3
commit 11d0ecac8b
10 changed files with 107 additions and 96 deletions

View File

@ -35,7 +35,7 @@ class CellEditorOverlay extends Component {
this.handleEditRawText = ::this.handleEditRawText this.handleEditRawText = ::this.handleEditRawText
this.handleSetRange = ::this.handleSetRange this.handleSetRange = ::this.handleSetRange
const {cell: {name, type, queries, yRanges}} = props const {cell: {name, type, queries, axes}} = props
const queriesWorkingDraft = _.cloneDeep( const queriesWorkingDraft = _.cloneDeep(
queries.map(({queryConfig}) => ({...queryConfig, id: uuid.v4()})) queries.map(({queryConfig}) => ({...queryConfig, id: uuid.v4()}))
@ -47,9 +47,7 @@ class CellEditorOverlay extends Component {
queriesWorkingDraft, queriesWorkingDraft,
activeQueryIndex: 0, activeQueryIndex: 0,
isDisplayOptionsTabOpen: false, isDisplayOptionsTabOpen: false,
yRanges: { axes,
y: yRanges && yRanges.y ? yRanges.y : ['', ''],
},
} }
} }
@ -82,9 +80,12 @@ class CellEditorOverlay extends Component {
handleSetRange(e) { handleSetRange(e) {
const {min, max} = e.target.form const {min, max} = e.target.form
this.setState({ this.setState({
yRanges: { axes: {
y: [min.value, max.value], y: {
bounds: [min.value, max.value],
},
}, },
}) })
e.preventDefault() e.preventDefault()
@ -108,7 +109,7 @@ class CellEditorOverlay extends Component {
queriesWorkingDraft, queriesWorkingDraft,
cellWorkingType: type, cellWorkingType: type,
cellWorkingName: name, cellWorkingName: name,
yRanges, axes,
} = this.state } = this.state
const {cell} = this.props const {cell} = this.props
@ -125,7 +126,13 @@ class CellEditorOverlay extends Component {
} }
}) })
this.props.onSave({...cell, name, type, queries, yRanges}) this.props.onSave({
...cell,
name,
type,
queries,
axes,
})
} }
handleSelectGraphType(graphType) { handleSelectGraphType(graphType) {
@ -174,7 +181,7 @@ class CellEditorOverlay extends Component {
cellWorkingType, cellWorkingType,
isDisplayOptionsTabOpen, isDisplayOptionsTabOpen,
queriesWorkingDraft, queriesWorkingDraft,
yRanges, axes,
} = this.state } = this.state
const queryActions = { const queryActions = {
@ -205,7 +212,7 @@ class CellEditorOverlay extends Component {
cellType={cellWorkingType} cellType={cellWorkingType}
cellName={cellWorkingName} cellName={cellWorkingName}
editQueryStatus={editQueryStatus} editQueryStatus={editQueryStatus}
yRanges={yRanges} axes={axes}
views={[]} views={[]}
/> />
<div className="overlay-technology--editor"> <div className="overlay-technology--editor">
@ -221,7 +228,7 @@ class CellEditorOverlay extends Component {
selectedGraphType={cellWorkingType} selectedGraphType={cellWorkingType}
onSelectGraphType={this.handleSelectGraphType} onSelectGraphType={this.handleSelectGraphType}
onSetRange={this.handleSetRange} onSetRange={this.handleSetRange}
yRanges={yRanges} axes={axes}
/> />
: <QueryMaker : <QueryMaker
source={source} source={source}

View File

@ -7,26 +7,23 @@ const DisplayOptions = ({
selectedGraphType, selectedGraphType,
onSelectGraphType, onSelectGraphType,
onSetRange, onSetRange,
yRanges, axes,
}) => }) =>
<div className="display-options"> <div className="display-options">
<GraphTypeSelector <GraphTypeSelector
selectedGraphType={selectedGraphType} selectedGraphType={selectedGraphType}
onSelectGraphType={onSelectGraphType} onSelectGraphType={onSelectGraphType}
/> />
<Ranger onSetRange={onSetRange} yRanges={yRanges} /> <Ranger onSetRange={onSetRange} axes={axes} />
</div> </div>
const {array, func, shape, string} = PropTypes const {func, shape, string} = PropTypes
DisplayOptions.propTypes = { DisplayOptions.propTypes = {
selectedGraphType: string.isRequired, selectedGraphType: string.isRequired,
onSelectGraphType: func.isRequired, onSelectGraphType: func.isRequired,
onSetRange: func.isRequired, onSetRange: func.isRequired,
yRanges: shape({ axes: shape({}).isRequired,
y: array,
y2: array,
}).isRequired,
} }
export default DisplayOptions export default DisplayOptions

View File

@ -1,29 +1,34 @@
import React, {PropTypes} from 'react' import React, {PropTypes} from 'react'
import _ from 'lodash'
const Ranger = ({onSetRange, yRanges}) => const Ranger = ({onSetRange, axes}) =>
<div className="display-options--cell"> <div className="display-options--cell">
<h5 className="display-options--header">Y Axis Controls</h5> <h5 className="display-options--header">Y Axis Controls</h5>
<form autoComplete="off"> <form autoComplete="off">
<div className="display-options--row"> <div className="display-options--row">
<label htmlFor="min" style={{width: '40px'}}>Min</label> <label htmlFor="min" style={{width: '40px'}}>
Min
</label>
<input <input
className="form-control input-sm" className="form-control input-sm"
type="text" type="number"
name="min" name="min"
id="min" id="min"
value={yRanges.y[0]} value={_.get(axes, ['y', 'bounds', '0'], '')}
onChange={onSetRange} onChange={onSetRange}
placeholder="auto" placeholder="auto"
/> />
</div> </div>
<div className="display-options--row"> <div className="display-options--row">
<label htmlFor="max" style={{width: '40px'}}>Max</label> <label htmlFor="max" style={{width: '40px'}}>
Max
</label>
<input <input
className="form-control input-sm" className="form-control input-sm"
type="text" type="number"
name="max" name="max"
id="max" id="max"
value={yRanges.y[1]} value={_.get(axes, ['y', 'bounds', '1'], '')}
onChange={onSetRange} onChange={onSetRange}
placeholder="auto" placeholder="auto"
/> />
@ -35,9 +40,10 @@ const {array, func, shape} = PropTypes
Ranger.propTypes = { Ranger.propTypes = {
onSetRange: func.isRequired, onSetRange: func.isRequired,
yRanges: shape({ axes: shape({
y: array, y: shape({
y2: array, bounds: array,
}),
}).isRequired, }).isRequired,
} }

View File

@ -1,23 +1,18 @@
import React, {PropTypes} from 'react' import React, {PropTypes} from 'react'
import Table from './Table' import Table from './Table'
import AutoRefresh from 'shared/components/AutoRefresh' import RefreshingGraph from 'shared/components/RefreshingGraph'
import LineGraph from 'shared/components/LineGraph'
import SingleStat from 'shared/components/SingleStat'
const RefreshingLineGraph = AutoRefresh(LineGraph)
const RefreshingSingleStat = AutoRefresh(SingleStat)
const VisView = ({ const VisView = ({
axes,
view, view,
queries, queries,
yRanges,
cellType, cellType,
templates, templates,
autoRefresh, autoRefresh,
heightPixels, heightPixels,
editQueryStatus, editQueryStatus,
activeQueryIndex, activeQueryIndex,
isInDataExplorer,
}) => { }) => {
const activeQuery = queries[activeQueryIndex] const activeQuery = queries[activeQueryIndex]
const defaultQuery = queries[0] const defaultQuery = queries[0]
@ -41,42 +36,23 @@ const VisView = ({
) )
} }
if (cellType === 'single-stat') {
return (
<RefreshingSingleStat
queries={queries.length ? [queries[0]] : []}
autoRefresh={autoRefresh}
templates={templates}
/>
)
}
const displayOptions = {
stepPlot: cellType === 'line-stepplot',
stackedGraph: cellType === 'line-stacked',
}
return ( return (
<RefreshingLineGraph <RefreshingGraph
axes={axes}
type={cellType}
queries={queries} queries={queries}
yRanges={yRanges}
templates={templates} templates={templates}
cellHeight={heightPixels}
autoRefresh={autoRefresh} autoRefresh={autoRefresh}
activeQueryIndex={activeQueryIndex}
isInDataExplorer={isInDataExplorer}
showSingleStat={cellType === 'line-plus-single-stat'}
isBarGraph={cellType === 'bar'}
displayOptions={displayOptions}
editQueryStatus={editQueryStatus}
/> />
) )
} }
const {arrayOf, bool, func, number, shape, string} = PropTypes const {arrayOf, func, number, shape, string} = PropTypes
VisView.propTypes = { VisView.propTypes = {
view: string.isRequired, view: string.isRequired,
yRanges: shape(), axes: shape().isRequired,
queries: arrayOf(shape()).isRequired, queries: arrayOf(shape()).isRequired,
cellType: string, cellType: string,
templates: arrayOf(shape()), templates: arrayOf(shape()),
@ -84,7 +60,6 @@ VisView.propTypes = {
heightPixels: number, heightPixels: number,
editQueryStatus: func.isRequired, editQueryStatus: func.isRequired,
activeQueryIndex: number, activeQueryIndex: number,
isInDataExplorer: bool,
} }
export default VisView export default VisView

View File

@ -6,7 +6,7 @@ import VisView from 'src/data_explorer/components/VisView'
import {GRAPH, TABLE} from 'shared/constants' import {GRAPH, TABLE} from 'shared/constants'
import _ from 'lodash' import _ from 'lodash'
const {arrayOf, bool, func, number, shape, string} = PropTypes const {array, arrayOf, bool, func, number, shape, string} = PropTypes
const META_QUERY_REGEX = /^show/i const META_QUERY_REGEX = /^show/i
const Visualization = React.createClass({ const Visualization = React.createClass({
@ -26,7 +26,11 @@ const Visualization = React.createClass({
heightPixels: number, heightPixels: number,
editQueryStatus: func.isRequired, editQueryStatus: func.isRequired,
views: arrayOf(string).isRequired, views: arrayOf(string).isRequired,
yRanges: shape(), axes: shape({
y: shape({
bounds: array,
}),
}),
}, },
contextTypes: { contextTypes: {
@ -77,9 +81,9 @@ const Visualization = React.createClass({
render() { render() {
const { const {
axes,
views, views,
height, height,
yRanges,
cellType, cellType,
cellName, cellName,
timeRange, timeRange,
@ -118,7 +122,7 @@ const Visualization = React.createClass({
> >
<VisView <VisView
view={view} view={view}
yRanges={yRanges} axes={axes}
queries={queries} queries={queries}
templates={templates} templates={templates}
cellType={cellType} cellType={cellType}

View File

@ -4,6 +4,7 @@ import {fetchTimeSeriesAsync} from 'shared/actions/timeSeries'
import {removeUnselectedTemplateValues} from 'src/dashboards/constants' import {removeUnselectedTemplateValues} from 'src/dashboards/constants'
const { const {
array,
arrayOf, arrayOf,
bool, bool,
element, element,
@ -43,9 +44,11 @@ const AutoRefresh = ComposedComponent => {
text: string, text: string,
}).isRequired }).isRequired
).isRequired, ).isRequired,
yRanges: shape({ axes: shape({
y: arrayOf(string), bounds: shape({
y2: arrayOf(string), y: array,
y2: array,
}),
}), }),
editQueryStatus: func, editQueryStatus: func,
}, },
@ -173,7 +176,8 @@ const AutoRefresh = ComposedComponent => {
} }
if ( if (
this._noResultsForQuery(timeSeries) || !this.state.lastQuerySuccessful this._noResultsForQuery(timeSeries) ||
!this.state.lastQuerySuccessful
) { ) {
return this.renderNoResults() return this.renderNoResults()
} }

View File

@ -54,7 +54,7 @@ export default class Dygraph extends Component {
const timeSeries = this.getTimeSeries() const timeSeries = this.getTimeSeries()
// dygraphSeries is a legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'}; // dygraphSeries is a legend label and its corresponding y-axis e.g. {legendLabel1: 'y', legendLabel2: 'y2'};
const { const {
ranges, axes,
dygraphSeries, dygraphSeries,
ruleValues, ruleValues,
overrideLineColors, overrideLineColors,
@ -71,6 +71,9 @@ export default class Dygraph extends Component {
finalLineColors = LINE_COLORS finalLineColors = LINE_COLORS
} }
const yAxis = _.get(axes, ['y', 'bounds'], [null, null])
const y2Axis = _.get(axes, ['y2', 'bounds'], undefined)
const defaultOptions = { const defaultOptions = {
plugins: [ plugins: [
new Dygraphs.Plugins.Crosshair({ new Dygraphs.Plugins.Crosshair({
@ -92,10 +95,10 @@ export default class Dygraph extends Component {
series: dygraphSeries, series: dygraphSeries,
axes: { axes: {
y: { y: {
valueRange: getRange(timeSeries, ranges && ranges.y, ruleValues), valueRange: getRange(timeSeries, yAxis, ruleValues),
}, },
y2: { y2: {
valueRange: getRange(timeSeries, ranges && ranges.y2), valueRange: getRange(timeSeries, y2Axis),
}, },
}, },
highlightSeriesOpts: { highlightSeriesOpts: {
@ -235,7 +238,7 @@ export default class Dygraph extends Component {
componentDidUpdate() { componentDidUpdate() {
const { const {
labels, labels,
ranges, axes,
options, options,
dygraphSeries, dygraphSeries,
ruleValues, ruleValues,
@ -249,16 +252,19 @@ export default class Dygraph extends Component {
) )
} }
const y = _.get(axes, ['y', 'bounds'], [null, null])
const y2 = _.get(axes, ['y2', 'bounds'], undefined)
const timeSeries = this.getTimeSeries() const timeSeries = this.getTimeSeries()
const updateOptions = { const updateOptions = {
labels, labels,
file: timeSeries, file: timeSeries,
axes: { axes: {
y: { y: {
valueRange: getRange(timeSeries, ranges && ranges.y, ruleValues), valueRange: getRange(timeSeries, y, ruleValues),
}, },
y2: { y2: {
valueRange: getRange(timeSeries, ranges && ranges.y2), valueRange: getRange(timeSeries, y2),
}, },
}, },
stepPlot: options.stepPlot, stepPlot: options.stepPlot,
@ -343,7 +349,7 @@ export default class Dygraph extends Component {
isAscending={isAscending} isAscending={isAscending}
onSnip={this.handleSnipLabel} onSnip={this.handleSnipLabel}
onSort={this.handleSortLegend} onSort={this.handleSortLegend}
legendRef={el => this.legendRef = el} legendRef={el => (this.legendRef = el)}
onInputChange={this.handleLegendInputChange} onInputChange={this.handleLegendInputChange}
onToggleFilter={this.handleToggleFilter} onToggleFilter={this.handleToggleFilter}
/> />
@ -359,12 +365,16 @@ export default class Dygraph extends Component {
} }
} }
const {array, arrayOf, func, bool, shape, string} = PropTypes const {array, bool, func, shape, string} = PropTypes
Dygraph.propTypes = { Dygraph.propTypes = {
ranges: shape({ axes: shape({
y: arrayOf(string), y: shape({
y2: arrayOf(string), bounds: array,
}),
y2: shape({
bounds: array,
}),
}), }),
timeSeries: array.isRequired, timeSeries: array.isRequired,
labels: array.isRequired, labels: array.isRequired,

View File

@ -15,9 +15,13 @@ export default React.createClass({
displayName: 'LineGraph', displayName: 'LineGraph',
propTypes: { propTypes: {
data: arrayOf(shape({}).isRequired).isRequired, data: arrayOf(shape({}).isRequired).isRequired,
yRanges: shape({ axes: shape({
y: arrayOf(string), y: shape({
y2: arrayOf(string), bounds: array,
}),
y2: shape({
bounds: array,
}),
}), }),
title: string, title: string,
isFetchingInitially: bool, isFetchingInitially: bool,
@ -67,7 +71,8 @@ export default React.createClass({
componentWillUpdate(nextProps) { componentWillUpdate(nextProps) {
const {data, activeQueryIndex} = this.props const {data, activeQueryIndex} = this.props
if ( if (
data !== nextProps.data || activeQueryIndex !== nextProps.activeQueryIndex data !== nextProps.data ||
activeQueryIndex !== nextProps.activeQueryIndex
) { ) {
this._timeSeries = timeSeriesToDygraph( this._timeSeries = timeSeriesToDygraph(
nextProps.data, nextProps.data,
@ -80,7 +85,7 @@ export default React.createClass({
render() { render() {
const { const {
data, data,
yRanges, axes,
isFetchingInitially, isFetchingInitially,
isRefreshing, isRefreshing,
isGraphFilled, isGraphFilled,
@ -159,6 +164,7 @@ export default React.createClass({
> >
{isRefreshing ? this.renderSpinner() : null} {isRefreshing ? this.renderSpinner() : null}
<Dygraph <Dygraph
axes={axes}
containerStyle={{width: '100%', height: '100%'}} containerStyle={{width: '100%', height: '100%'}}
overrideLineColors={ overrideLineColors={
showSingleStat ? singleStatLineColors : overrideLineColors showSingleStat ? singleStatLineColors : overrideLineColors
@ -169,7 +175,6 @@ export default React.createClass({
labels={labels} labels={labels}
options={showSingleStat ? singleStatOptions : options} options={showSingleStat ? singleStatOptions : options}
dygraphSeries={dygraphSeries} dygraphSeries={dygraphSeries}
ranges={yRanges}
ruleValues={ruleValues} ruleValues={ruleValues}
synchronizer={synchronizer} synchronizer={synchronizer}
timeRange={timeRange} timeRange={timeRange}
@ -182,7 +187,9 @@ export default React.createClass({
'single-stat--small': cellHeight === SMALL_CELL_HEIGHT, 'single-stat--small': cellHeight === SMALL_CELL_HEIGHT,
})} })}
> >
<span className="single-stat--shadow">{roundedValue}</span> <span className="single-stat--shadow">
{roundedValue}
</span>
</span> </span>
</div> </div>
: null} : null}

View File

@ -15,7 +15,7 @@ const RefreshingGraph = ({
type, type,
queries, queries,
cellHeight, cellHeight,
yRanges, axes,
}) => { }) => {
if (type === 'single-stat') { if (type === 'single-stat') {
return ( return (
@ -43,7 +43,7 @@ const RefreshingGraph = ({
isBarGraph={type === 'bar'} isBarGraph={type === 'bar'}
displayOptions={displayOptions} displayOptions={displayOptions}
synchronizer={synchronizer} synchronizer={synchronizer}
yRanges={yRanges} axes={axes}
/> />
) )
} }
@ -59,11 +59,8 @@ RefreshingGraph.propTypes = {
synchronizer: func, synchronizer: func,
type: string.isRequired, type: string.isRequired,
queries: arrayOf(shape()).isRequired, queries: arrayOf(shape()).isRequired,
cellHeight: number.isRequired, cellHeight: number,
yRanges: shape({ axes: shape(),
y: arrayOf(string),
y2: arrayOf(string),
}),
} }
export default RefreshingGraph export default RefreshingGraph

View File

@ -1,8 +1,12 @@
const PADDING_FACTOR = 0.1 const PADDING_FACTOR = 0.1
const considerZero = (userNumber, number) => { const considerZero = (userNumber, number) => {
if (typeof userNumber === 'number') { if (userNumber === '') {
return userNumber return null
}
if (userNumber) {
return +userNumber
} }
return number return number