diff --git a/superset/assets/javascripts/explore/components/QueryAndSaveBtns.jsx b/superset/assets/javascripts/explore/components/QueryAndSaveBtns.jsx
index 1a5213934..d6e25a052 100644
--- a/superset/assets/javascripts/explore/components/QueryAndSaveBtns.jsx
+++ b/superset/assets/javascripts/explore/components/QueryAndSaveBtns.jsx
@@ -13,7 +13,7 @@ export default function QueryAndSaveBtns({ canAdd, onQuery }) {
return (
-
}
>
- { this.chartContainerRef = ref; }}
- className={this.props.viz_type}
- />
+ {!this.props.isChartLoading &&
+
{ this.chartContainerRef = ref; }}
+ className={this.props.viz_type}
+ />
+ }
);
@@ -176,6 +185,8 @@ function mapStateToProps(state) {
standalone_endpoint: state.viz.standalone_endpoint,
query: state.viz.query,
column_formats: state.viz.column_formats,
+ data: state.viz.data,
+ isChartLoading: state.isChartLoading,
};
}
diff --git a/superset/assets/javascripts/explorev2/components/ExploreViewContainer.jsx b/superset/assets/javascripts/explorev2/components/ExploreViewContainer.jsx
index 253b5beb3..6ac5d6e6a 100644
--- a/superset/assets/javascripts/explorev2/components/ExploreViewContainer.jsx
+++ b/superset/assets/javascripts/explorev2/components/ExploreViewContainer.jsx
@@ -1,9 +1,24 @@
+/* eslint camelcase: 0 */
import React from 'react';
+import { bindActionCreators } from 'redux';
+import * as actions from '../actions/exploreActions';
+import { connect } from 'react-redux';
import ChartContainer from './ChartContainer';
import ControlPanelsContainer from './ControlPanelsContainer';
import QueryAndSaveBtns from '../../explore/components/QueryAndSaveBtns';
+const $ = require('jquery');
-export default class ExploreViewContainer extends React.Component {
+const propTypes = {
+ form_data: React.PropTypes.object.isRequired,
+ actions: React.PropTypes.object.isRequired,
+ slice_id: React.PropTypes.string.isRequired,
+ slice_name: React.PropTypes.string.isRequired,
+ datasource_id: React.PropTypes.number.isRequired,
+ datasource_type: React.PropTypes.string.isRequired,
+};
+
+
+class ExploreViewContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
@@ -11,11 +26,43 @@ export default class ExploreViewContainer extends React.Component {
};
}
+ onQuery() {
+ const data = {};
+ const form_data = this.props.form_data;
+ Object.keys(form_data).forEach((field) => {
+ // filter out null fields
+ if (form_data[field] !== null) {
+ data[field] = form_data[field];
+ }
+ });
+ // V2 tag temporarily for updating url
+ // Todo: remove after launch
+ data.V2 = true;
+ data.datasource_id = this.props.datasource_id;
+ data.datasource_type = this.props.datasource_type;
+ this.queryFormData(data);
+
+ const params = $.param(data, true);
+ this.updateUrl(params);
+ }
+
getHeight() {
const navHeight = 90;
return `${window.innerHeight - navHeight}px`;
}
+ updateUrl(params) {
+ const baseUrl =
+ `/superset/explore/${this.props.datasource_type}/${this.props.datasource_id}/`;
+ const newEndpoint = `${baseUrl}?${params}`;
+ history.pushState({}, document.title, newEndpoint);
+ }
+
+ queryFormData(data) {
+ this.props.actions.updateExplore(
+ this.props.datasource_type, this.props.datasource_id, data);
+ }
+
render() {
return (
{}}
+ onQuery={this.onQuery.bind(this)}
/>
-
+
{
const defaultProps = {
canAdd: 'True',
- onQuery: () => {},
+ onQuery: sinon.spy(),
};
// It must render
@@ -32,5 +33,11 @@ describe('QueryAndSaveButtons', () => {
expect(wrapper.find('button').contains(' Query')).to.eql(true);
expect(wrapper.find('button').contains(' Save as')).to.eql(true);
});
+
+ it('calls onQuery when query button is clicked', () => {
+ const queryButton = wrapper.find('#query_button');
+ queryButton.simulate('click');
+ expect(defaultProps.onQuery.called).to.eql(true);
+ });
});
});
diff --git a/superset/views.py b/superset/views.py
index 841c968b4..486008d6e 100755
--- a/superset/views.py
+++ b/superset/views.py
@@ -1244,7 +1244,8 @@ class Superset(BaseSupersetView):
viz_type = args.get('viz_type', 'table')
datasource = SourceRegistry.get_datasource(
datasource_type, datasource_id, db.session)
- viz_obj = viz.viz_types[viz_type](datasource, request.args)
+ viz_obj = viz.viz_types[viz_type](
+ datasource, request.args if request.args else args)
return viz_obj
@has_access
@@ -1253,6 +1254,24 @@ class Superset(BaseSupersetView):
viz_obj = self.get_viz(slice_id)
return redirect(viz_obj.get_url(**request.args))
+ @log_this
+ @has_access_api
+ @expose(
+ "/update_explore///", methods=['POST'])
+ def update_explore(self, datasource_type, datasource_id):
+ """Send back new viz on POST request for updating update explore view"""
+ form_data = json.loads(request.form.get('data'))
+ error_redirect = '/slicemodelview/list/'
+ try:
+ viz_obj = self.get_viz(
+ datasource_type=datasource_type,
+ datasource_id=datasource_id,
+ args=form_data)
+ except Exception as e:
+ flash('{}'.format(e), "alert")
+ return redirect(error_redirect)
+ return viz_obj.get_json()
+
@has_access_api
@expose("/explore_json///")
def explore_json(self, datasource_type, datasource_id):
diff --git a/tests/core_tests.py b/tests/core_tests.py
index 70e8d6459..5b3e23348 100644
--- a/tests/core_tests.py
+++ b/tests/core_tests.py
@@ -97,6 +97,25 @@ class CoreTests(SupersetTestCase):
assert_admin_view_menus_in('Alpha', self.assertNotIn)
assert_admin_view_menus_in('Gamma', self.assertNotIn)
+ def test_update_explore(self):
+ self.login(username='admin')
+ tbl_id = self.table_ids.get('energy_usage')
+ data = json.dumps({
+ 'viz_type': 'sankey',
+ 'groupby': ['source', 'target'],
+ 'metrics': ['sum__value'],
+ 'row_limit': 5000,
+ 'flt_col_0': 'source',
+ 'datasource_name': 'energy_usage',
+ 'datasource_id': tbl_id,
+ 'datasource_type': 'table',
+ 'previous_viz_type': 'sankey'
+ })
+ response = self.client.post('/superset/update_explore/table/{}/'.format(tbl_id),
+ data=dict(data=data))
+ assert response.status_code == 200
+ self.logout()
+
def test_save_slice(self):
self.login(username='admin')
slice_id = self.get_slice("Energy Sankey", db.session).id