-
Notifications
You must be signed in to change notification settings - Fork 14.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[explore] adding y_axis_bounds to force Y axis bounds #2878
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import React from 'react'; | ||
import PropTypes from 'prop-types'; | ||
import { Col, Row, FormGroup, FormControl } from 'react-bootstrap'; | ||
import ControlHeader from '../ControlHeader'; | ||
|
||
const propTypes = { | ||
name: PropTypes.string.isRequired, | ||
label: PropTypes.string, | ||
description: PropTypes.string, | ||
onChange: PropTypes.func, | ||
value: PropTypes.array, | ||
}; | ||
|
||
const defaultProps = { | ||
label: null, | ||
description: null, | ||
onChange: () => {}, | ||
value: [null, null], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i wonder if it would be more clear to store the min/max values as separate keys rather than an array. then we don't have to keep track of which value in the array is min or max. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about this and wanted to minimize the |
||
}; | ||
|
||
export default class BoundsControl extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = { | ||
minMax: [ | ||
props.value[0] === null ? '' : props.value[0], | ||
props.value[1] === null ? '' : props.value[1], | ||
], | ||
}; | ||
this.onChange = this.onChange.bind(this); | ||
this.onMinChange = this.onMinChange.bind(this); | ||
this.onMaxChange = this.onMaxChange.bind(this); | ||
} | ||
onMinChange(event) { | ||
this.setState({ | ||
minMax: [ | ||
event.target.value, | ||
this.state.minMax[1], | ||
], | ||
}, this.onChange); | ||
} | ||
onMaxChange(event) { | ||
this.setState({ | ||
minMax: [ | ||
this.state.minMax[0], | ||
event.target.value, | ||
], | ||
}, this.onChange); | ||
} | ||
onChange() { | ||
const mm = this.state.minMax; | ||
const errors = []; | ||
if (mm[0] && isNaN(mm[0])) { | ||
errors.push('`From` value should be numeric or empty'); | ||
} | ||
if (mm[1] && isNaN(mm[1])) { | ||
errors.push('`To` value should be numeric or empty'); | ||
} | ||
if (errors.length === 0) { | ||
this.props.onChange([parseFloat(mm[0]), parseFloat(mm[1])], errors); | ||
} else { | ||
this.props.onChange([null, null], errors); | ||
} | ||
} | ||
render() { | ||
return ( | ||
<div> | ||
<ControlHeader {...this.props} /> | ||
<FormGroup controlId="formInlineName" bsSize="small"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what do we use |
||
<Row> | ||
<Col xs={6} key="from"> | ||
<FormControl | ||
type="text" | ||
placeholder="From" | ||
onChange={this.onMinChange} | ||
value={this.state.minMax[0]} | ||
/> | ||
</Col> | ||
<Col xs={6} key="to"> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should we use |
||
<FormControl | ||
type="text" | ||
placeholder="To" | ||
onChange={this.onMaxChange} | ||
value={this.state.minMax[1]} | ||
/> | ||
</Col> | ||
</Row> | ||
</FormGroup> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
BoundsControl.propTypes = propTypes; | ||
BoundsControl.defaultProps = defaultProps; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,7 +65,17 @@ export const controls = { | |
}), | ||
description: 'One or many metrics to display', | ||
}, | ||
|
||
y_axis_bounds: { | ||
type: 'BoundsControl', | ||
label: 'Y Axis Bounds', | ||
default: [null, null], | ||
description: ( | ||
'Bounds for the Y axis. When left empty, the bounds are ' + | ||
'dynamically defined based on the min/max of the data. Note that ' + | ||
"this feature will only expand the axis range. It won't " + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. looks like you are using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. the linter is pretty picky about this already, I'd side with the linter on this one There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I tried going double quotes and the linter wanted single quotes in the first 2 lines... |
||
"narrow the data's extent." | ||
), | ||
}, | ||
order_by_cols: { | ||
type: 'SelectControl', | ||
multi: true, | ||
|
@@ -751,7 +761,7 @@ export const controls = { | |
x_axis_format: { | ||
type: 'SelectControl', | ||
freeForm: true, | ||
label: 'X axis format', | ||
label: 'X Axis Format', | ||
renderTrigger: true, | ||
default: 'smart_date', | ||
choices: TIME_STAMP_OPTIONS, | ||
|
@@ -761,7 +771,7 @@ export const controls = { | |
y_axis_format: { | ||
type: 'SelectControl', | ||
freeForm: true, | ||
label: 'Y axis format', | ||
label: 'Y Axis Format', | ||
renderTrigger: true, | ||
default: '.3s', | ||
choices: D3_TIME_FORMAT_OPTIONS, | ||
|
@@ -771,7 +781,7 @@ export const controls = { | |
y_axis_2_format: { | ||
type: 'SelectControl', | ||
freeForm: true, | ||
label: 'Right axis format', | ||
label: 'Right Axis Format', | ||
default: '.3s', | ||
choices: D3_TIME_FORMAT_OPTIONS, | ||
description: D3_FORMAT_DOCS, | ||
|
@@ -937,14 +947,6 @@ export const controls = { | |
'point in time', | ||
}, | ||
|
||
y_axis_zero: { | ||
type: 'CheckboxControl', | ||
label: 'Y Axis Zero', | ||
default: false, | ||
renderTrigger: true, | ||
description: 'Force the Y axis to start at 0 instead of the minimum value', | ||
}, | ||
|
||
y_log_scale: { | ||
type: 'CheckboxControl', | ||
label: 'Y Log Scale', | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,6 +32,11 @@ export function getControlsState(state, form_data) { | |
const formData = Object.assign({}, form_data); | ||
const vizType = formData.viz_type || 'table'; | ||
|
||
// Control reacffectation for deprecation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could give more details with this comment: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. creating |
||
if (formData.y_axis_zero) { | ||
formData.y_axis_bounds = [0, null]; | ||
} | ||
|
||
const controlNames = getControlNames(vizType, state.datasource.type); | ||
|
||
const viz = visTypes[vizType]; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* eslint-disable no-unused-expressions */ | ||
import React from 'react'; | ||
import { FormControl } from 'react-bootstrap'; | ||
import sinon from 'sinon'; | ||
import { expect } from 'chai'; | ||
import { describe, it, beforeEach } from 'mocha'; | ||
import { mount } from 'enzyme'; | ||
|
||
import BoundsControl from '../../../../javascripts/explore/components/controls/BoundsControl'; | ||
|
||
const defaultProps = { | ||
name: 'y_axis_bounds', | ||
label: 'Bounds of the y axis', | ||
onChange: sinon.spy(), | ||
}; | ||
|
||
describe('BoundsControl', () => { | ||
let wrapper; | ||
|
||
beforeEach(() => { | ||
wrapper = mount(<BoundsControl {...defaultProps} />); | ||
}); | ||
|
||
it('renders two FormControls', () => { | ||
expect(wrapper.find(FormControl)).to.have.lengthOf(2); | ||
}); | ||
|
||
it('errors on non-numeric', () => { | ||
wrapper.find(FormControl).first().simulate('change', { target: { value: 's' } }); | ||
expect(defaultProps.onChange.calledWith([null, null])).to.be.true; | ||
expect(defaultProps.onChange.getCall(0).args[1][0]).to.contain('value should be numeric'); | ||
}); | ||
it('casts to numeric', () => { | ||
wrapper.find(FormControl).first().simulate('change', { target: { value: '1' } }); | ||
wrapper.find(FormControl).last().simulate('change', { target: { value: '5' } }); | ||
expect(defaultProps.onChange.calledWith([1, 5])).to.be.true; | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -306,9 +306,13 @@ function nvd3Vis(slice, payload) { | |
if ((vizType === 'line' || vizType === 'area') && fd.rich_tooltip) { | ||
chart.useInteractiveGuideline(true); | ||
} | ||
if (fd.y_axis_zero) { | ||
chart.forceY([0]); | ||
} else if (fd.y_log_scale) { | ||
if ( | ||
chart.forceY && | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. i think we should move this up to L309 |
||
fd.y_axis_bounds && | ||
(fd.y_axis_bounds[0] !== null || fd.y_axis_bounds[1] !== null)) { | ||
chart.forceY(fd.y_axis_bounds); | ||
} | ||
if (fd.y_log_scale) { | ||
chart.yScale(d3.scale.log()); | ||
} | ||
if (fd.x_log_scale) { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
in what case will
controlName
be falsey here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I introduced support for
null
invisTypes.jsx
as a way to use half of the grid