Skip to content
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

Merged
merged 2 commits into from
May 31, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions superset/assets/javascripts/explore/components/Control.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import SelectControl from './controls/SelectControl';
import TextAreaControl from './controls/TextAreaControl';
import TextControl from './controls/TextControl';
import VizTypeControl from './controls/VizTypeControl';
import BoundsControl from './controls/BoundsControl';

const controlMap = {
CheckboxControl,
Expand All @@ -17,6 +18,7 @@ const controlMap = {
TextAreaControl,
TextControl,
VizTypeControl,
BoundsControl,
};
const controlTypes = Object.keys(controlMap);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ const propTypes = {
controls: PropTypes.object.isRequired,
form_data: PropTypes.object.isRequired,
isDatasourceMetaLoading: PropTypes.bool.isRequired,
y_axis_zero: PropTypes.any,
};

class ControlPanelsContainer extends React.Component {
Expand Down Expand Up @@ -65,14 +64,15 @@ class ControlPanelsContainer extends React.Component {
<ControlRow
key={`controlsetrow-${i}`}
controls={controlSets.map(controlName => (
<Control
name={controlName}
key={`control-${controlName}`}
value={this.props.form_data[controlName]}
validationErrors={this.props.controls[controlName].validationErrors}
actions={this.props.actions}
{...this.getControlData(controlName)}
/>
controlName &&
Copy link

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?

Copy link
Member Author

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 in visTypes.jsx as a way to use half of the grid

<Control
name={controlName}
key={`control-${controlName}`}
value={this.props.form_data[controlName]}
validationErrors={this.props.controls[controlName].validationErrors}
actions={this.props.actions}
{...this.getControlData(controlName)}
/>
))}
/>
))}
Expand Down
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],
Copy link

Choose a reason for hiding this comment

The 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.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought about this and wanted to minimize the formData payload to a minimum, at least until we use POST instead of GET throughout.

};

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">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what do we use controlId for?

<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">
Copy link

@ascott ascott May 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we use min and max as the column keys/placeholders for consistency?

<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
Expand Up @@ -63,7 +63,7 @@ export default class VizTypeControl extends React.PureComponent {
const rows = [];
for (let i = 0; i <= filteredVizTypes.length; i += imgPerRow) {
rows.push(
<Row>
<Row key={`row-${i}`}>
{filteredVizTypes.slice(i, i + imgPerRow).map(vt => (
<Col md={3} key={`grid-col-${vt}`}>
{this.renderVizType(vt)}
Expand Down
26 changes: 14 additions & 12 deletions superset/assets/javascripts/explore/stores/controls.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 " +
Copy link

@ascott ascott May 31, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks like you are using " since you have a ' in the string. would be more consistent to use " for every line of this string.

Copy link
Member Author

Choose a reason for hiding this comment

The 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

Copy link
Member Author

Choose a reason for hiding this comment

The 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,
Expand Down Expand Up @@ -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,
Expand All @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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',
Expand Down
5 changes: 5 additions & 0 deletions superset/assets/javascripts/explore/stores/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could give more details with this comment: Set y_axis_bounds for viz's that once used the now deprecated y_axis_zero control.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

creating function handleDeprecatedControls(formData) to clarify and segment things

if (formData.y_axis_zero) {
formData.y_axis_bounds = [0, null];
}

const controlNames = getControlNames(vizType, state.datasource.type);

const viz = visTypes[vizType];
Expand Down
49 changes: 32 additions & 17 deletions superset/assets/javascripts/explore/stores/visTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -128,12 +128,17 @@ const visTypes = {
label: 'Chart Options',
controlSetRows: [
['show_brush', 'show_legend'],
['rich_tooltip', 'y_axis_zero'],
['y_log_scale', 'contribution'],
['rich_tooltip', null],
['show_markers', 'x_axis_showminmax'],
['line_interpolation'],
['x_axis_format', 'y_axis_format'],
['x_axis_label', 'y_axis_label'],
['line_interpolation', 'contribution'],
],
},
{
label: 'Axes',
controlSetRows: [
['x_axis_label', 'x_axis_format'],
['y_axis_label', 'y_axis_bounds'],
['y_axis_format', 'y_log_scale'],
],
},
sections.NVD3TimeSeries[1],
Expand Down Expand Up @@ -185,13 +190,18 @@ const visTypes = {
label: 'Chart Options',
controlSetRows: [
['show_brush', 'show_legend', 'show_bar_value'],
['rich_tooltip', 'y_axis_zero'],
['y_log_scale', 'contribution'],
['x_axis_format', 'y_axis_format'],
['rich_tooltip', 'contribution'],
['line_interpolation', 'bar_stacked'],
['x_axis_showminmax', 'bottom_margin'],
['bottom_margin', 'show_controls'],
],
},
{
label: 'Axes',
controlSetRows: [
['x_axis_format', 'y_axis_format'],
['x_axis_showminmax', 'reduce_x_ticks'],
['x_axis_label', 'y_axis_label'],
['reduce_x_ticks', 'show_controls'],
['y_axis_bounds', 'y_log_scale'],
],
},
sections.NVD3TimeSeries[1],
Expand Down Expand Up @@ -222,11 +232,17 @@ const visTypes = {
label: 'Chart Options',
controlSetRows: [
['show_brush', 'show_legend'],
['rich_tooltip', 'y_axis_zero'],
['y_log_scale', 'contribution'],
['x_axis_format', 'y_axis_format'],
['x_axis_showminmax', 'show_controls'],
['line_interpolation', 'stacked_style'],
['rich_tooltip', 'contribution'],
['show_controls', null],
],
},
{
label: 'Axes',
controlSetRows: [
['x_axis_format', 'x_axis_showminmax'],
['y_axis_format', 'y_axis_bounds'],
['y_log_scale', null],
],
},
sections.NVD3TimeSeries[1],
Expand Down Expand Up @@ -401,10 +417,9 @@ const visTypes = {
{
label: 'Chart Options',
controlSetRows: [
['x_log_scale', 'y_log_scale'],
['show_legend'],
['max_bubble_size'],
['show_legend', 'max_bubble_size'],
['x_axis_label', 'y_axis_label'],
['x_log_scale', 'y_log_scale'],
],
},
],
Expand Down
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;
});
});
10 changes: 7 additions & 3 deletions superset/assets/visualizations/nvd3_vis.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 &&
Copy link

Choose a reason for hiding this comment

The 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) {
Expand Down