mirror of
https://github.com/apache/superset.git
synced 2026-04-20 16:44:46 +00:00
Get query button working in explorev2 (#1581)
* Get query buttonw working in explorev2 - Create new endpoint for updating explore viz - Send over new form_data when query button is pressed * Added endpoint test * Changes based on comments * Added docstring for endpoint, and query spec * Remove white space around docstring
This commit is contained in:
@@ -1,26 +1,17 @@
|
||||
/* eslint camelcase: 0 */
|
||||
const $ = window.$ = require('jquery');
|
||||
export const SET_DATASOURCE = 'SET_DATASOURCE';
|
||||
export const SET_FIELD_OPTIONS = 'SET_FIELD_OPTIONS';
|
||||
export const TOGGLE_SEARCHBOX = 'TOGGLE_SEARCHBOX';
|
||||
export const SET_FILTER_COLUMN_OPTS = 'SET_FILTER_COLUMN_OPTS';
|
||||
export const ADD_FILTER = 'ADD_FILTER';
|
||||
export const SET_FILTER = 'SET_FILTER';
|
||||
export const REMOVE_FILTER = 'REMOVE_FILTER';
|
||||
export const CHANGE_FILTER_FIELD = 'CHANGE_FILTER_FIELD';
|
||||
export const CHANGE_FILTER_OP = 'CHANGE_FILTER_OP';
|
||||
export const CHANGE_FILTER_VALUE = 'CHANGE_FILTER_VALUE';
|
||||
export const CLEAR_ALL_OPTS = 'CLEAR_ALL_OPTS';
|
||||
export const SET_DATASOURCE_TYPE = 'SET_DATASOURCE_TYPE';
|
||||
export const SET_FIELD_VALUE = 'SET_FIELD_VALUE';
|
||||
|
||||
export const SET_FIELD_OPTIONS = 'SET_FIELD_OPTIONS';
|
||||
export function setFieldOptions(options) {
|
||||
return { type: SET_FIELD_OPTIONS, options };
|
||||
}
|
||||
|
||||
export const CLEAR_ALL_OPTS = 'CLEAR_ALL_OPTS';
|
||||
export function clearAllOpts() {
|
||||
return { type: CLEAR_ALL_OPTS };
|
||||
}
|
||||
|
||||
export const SET_DATASOURCE_TYPE = 'SET_DATASOURCE_TYPE';
|
||||
export function setDatasourceType(datasourceType) {
|
||||
return { type: SET_DATASOURCE_TYPE, datasourceType };
|
||||
}
|
||||
@@ -62,26 +53,71 @@ export function fetchFieldOptions(datasourceId, datasourceType) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export const ADD_FILTER = 'ADD_FILTER';
|
||||
export function addFilter(filter) {
|
||||
return { type: ADD_FILTER, filter };
|
||||
}
|
||||
|
||||
export const REMOVE_FILTER = 'REMOVE_FILTER';
|
||||
export function removeFilter(filter) {
|
||||
return { type: REMOVE_FILTER, filter };
|
||||
}
|
||||
|
||||
export const CHANGE_FILTER_FIELD = 'CHANGE_FILTER_FIELD';
|
||||
export function changeFilterField(filter, field) {
|
||||
return { type: CHANGE_FILTER_FIELD, filter, field };
|
||||
}
|
||||
|
||||
export const CHANGE_FILTER_OP = 'CHANGE_FILTER_OP';
|
||||
export function changeFilterOp(filter, op) {
|
||||
return { type: CHANGE_FILTER_OP, filter, op };
|
||||
}
|
||||
|
||||
export const CHANGE_FILTER_VALUE = 'CHANGE_FILTER_VALUE';
|
||||
export function changeFilterValue(filter, value) {
|
||||
return { type: CHANGE_FILTER_VALUE, filter, value };
|
||||
}
|
||||
|
||||
export const SET_FIELD_VALUE = 'SET_FIELD_VALUE';
|
||||
export function setFieldValue(key, value) {
|
||||
return { type: SET_FIELD_VALUE, key, value };
|
||||
}
|
||||
|
||||
export const UPDATE_CHART = 'UPDATE_CHART';
|
||||
export function updateChart(viz) {
|
||||
return { type: UPDATE_CHART, viz };
|
||||
}
|
||||
|
||||
export const CHART_UPDATE_STARTED = 'CHART_UPDATE_STARTED';
|
||||
export function chartUpdateStarted() {
|
||||
return { type: CHART_UPDATE_STARTED };
|
||||
}
|
||||
|
||||
export const CHART_UPDATE_FAILED = 'CHART_UPDATE_FAILED ';
|
||||
export function chartUpdateFailed() {
|
||||
return { type: CHART_UPDATE_FAILED };
|
||||
}
|
||||
|
||||
export function updateExplore(datasource_type, datasource_id, form_data) {
|
||||
return function (dispatch) {
|
||||
dispatch(chartUpdateStarted);
|
||||
const updateUrl =
|
||||
`/superset/update_explore/${datasource_type}/${datasource_id}/`;
|
||||
|
||||
$.ajax({
|
||||
type: 'POST',
|
||||
url: updateUrl,
|
||||
data: {
|
||||
data: JSON.stringify(form_data),
|
||||
},
|
||||
success: (data) => {
|
||||
dispatch(updateChart(JSON.parse(data)));
|
||||
},
|
||||
error(error) {
|
||||
dispatch(chartUpdateFailed(error));
|
||||
},
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ const propTypes = {
|
||||
standalone_endpoint: PropTypes.string.isRequired,
|
||||
query: PropTypes.string.isRequired,
|
||||
column_formats: PropTypes.object,
|
||||
data: PropTypes.any,
|
||||
isChartLoading: PropTypes.bool,
|
||||
};
|
||||
|
||||
class ChartContainer extends React.Component {
|
||||
@@ -29,30 +31,34 @@ class ChartContainer extends React.Component {
|
||||
}
|
||||
|
||||
componentWillMount() {
|
||||
this.setState({ mockSlice: this.getMockedSliceObject() });
|
||||
this.setState({ mockSlice: this.getMockedSliceObject(this.props) });
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.renderVis();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps) {
|
||||
this.setState({ mockSlice: this.getMockedSliceObject(nextProps) });
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.renderVis();
|
||||
}
|
||||
|
||||
getMockedSliceObject() {
|
||||
getMockedSliceObject(props) {
|
||||
return {
|
||||
viewSqlQuery: this.props.query,
|
||||
viewSqlQuery: props.query,
|
||||
|
||||
data: {
|
||||
csv_endpoint: this.props.csv_endpoint,
|
||||
json_endpoint: this.props.json_endpoint,
|
||||
standalone_endpoint: this.props.standalone_endpoint,
|
||||
csv_endpoint: props.csv_endpoint,
|
||||
json_endpoint: props.json_endpoint,
|
||||
standalone_endpoint: props.standalone_endpoint,
|
||||
},
|
||||
|
||||
containerId: this.props.containerId,
|
||||
containerId: props.containerId,
|
||||
|
||||
jsonEndpoint: () => this.props.json_endpoint,
|
||||
jsonEndpoint: () => props.json_endpoint,
|
||||
|
||||
container: {
|
||||
html: (data) => {
|
||||
@@ -66,7 +72,7 @@ class ChartContainer extends React.Component {
|
||||
// should call callback to adjust height of chart
|
||||
$(this.state.selector).css(dim, size);
|
||||
},
|
||||
height: () => parseInt(this.props.height, 10) - 100,
|
||||
height: () => parseInt(props.height, 10) - 100,
|
||||
|
||||
show: () => { this.render(); },
|
||||
|
||||
@@ -78,7 +84,7 @@ class ChartContainer extends React.Component {
|
||||
|
||||
width: () => this.chartContainerRef.getBoundingClientRect().width,
|
||||
|
||||
height: () => parseInt(this.props.height, 10) - 100,
|
||||
height: () => parseInt(props.height, 10) - 100,
|
||||
|
||||
selector: this.state.selector,
|
||||
|
||||
@@ -128,6 +134,7 @@ class ChartContainer extends React.Component {
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
renderVis() {
|
||||
visMap[this.props.viz_type](this.state.mockSlice).render();
|
||||
}
|
||||
@@ -152,11 +159,13 @@ class ChartContainer extends React.Component {
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div
|
||||
id={this.props.containerId}
|
||||
ref={(ref) => { this.chartContainerRef = ref; }}
|
||||
className={this.props.viz_type}
|
||||
/>
|
||||
{!this.props.isChartLoading &&
|
||||
<div
|
||||
id={this.props.containerId}
|
||||
ref={(ref) => { this.chartContainerRef = ref; }}
|
||||
className={this.props.viz_type}
|
||||
/>
|
||||
}
|
||||
</Panel>
|
||||
</div>
|
||||
);
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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 (
|
||||
<div
|
||||
@@ -29,10 +76,13 @@ export default class ExploreViewContainer extends React.Component {
|
||||
<div className="col-sm-4">
|
||||
<QueryAndSaveBtns
|
||||
canAdd="True"
|
||||
onQuery={() => {}}
|
||||
onQuery={this.onQuery.bind(this)}
|
||||
/>
|
||||
<br /><br />
|
||||
<ControlPanelsContainer />
|
||||
<ControlPanelsContainer
|
||||
actions={this.props.actions}
|
||||
form_data={this.props.form_data}
|
||||
/>
|
||||
</div>
|
||||
<div className="col-sm-8">
|
||||
<ChartContainer
|
||||
@@ -44,3 +94,25 @@ export default class ExploreViewContainer extends React.Component {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ExploreViewContainer.propTypes = propTypes;
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
datasource_id: state.datasource_id,
|
||||
datasource_type: state.datasource_type,
|
||||
form_data: state.viz.form_data,
|
||||
slice_id: state.viz.form_data.slice_id,
|
||||
slice_name: state.viz.form_data.slice_name,
|
||||
};
|
||||
}
|
||||
|
||||
function mapDispatchToProps(dispatch) {
|
||||
return {
|
||||
actions: bindActionCreators(actions, dispatch),
|
||||
};
|
||||
}
|
||||
|
||||
export { ControlPanelsContainer };
|
||||
|
||||
export default connect(mapStateToProps, mapDispatchToProps)(ExploreViewContainer);
|
||||
|
||||
@@ -7,7 +7,7 @@ import Select, { Creatable } from 'react-select';
|
||||
const propTypes = {
|
||||
name: PropTypes.string.isRequired,
|
||||
choices: PropTypes.array,
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.array]),
|
||||
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.array]).isRequired,
|
||||
label: PropTypes.string,
|
||||
description: PropTypes.string,
|
||||
onChange: PropTypes.func,
|
||||
|
||||
@@ -19,6 +19,7 @@ const bootstrappedState = Object.assign(initialState, {
|
||||
datasource_type: bootstrapData.datasource_type,
|
||||
viz: bootstrapData.viz,
|
||||
});
|
||||
|
||||
const store = createStore(exploreReducer, bootstrappedState,
|
||||
compose(applyMiddleware(thunk))
|
||||
);
|
||||
|
||||
@@ -68,6 +68,29 @@ export const exploreReducer = function (state, action) {
|
||||
{ viz: Object.assign({}, state.viz, { form_data: newFormData }) }
|
||||
);
|
||||
},
|
||||
[actions.UPDATE_CHART]() {
|
||||
const vizUpdates = {
|
||||
column_formats: action.viz.column_formats,
|
||||
json_endpoint: action.viz.json_endpoint,
|
||||
csv_endpoint: action.viz.csv_endpoint,
|
||||
standalone_endpoint: action.viz.standalone_endpoint,
|
||||
query: action.viz.query,
|
||||
data: action.viz.data,
|
||||
};
|
||||
return Object.assign(
|
||||
{},
|
||||
state,
|
||||
{
|
||||
viz: Object.assign({}, state.viz, vizUpdates),
|
||||
isChartLoading: false,
|
||||
});
|
||||
},
|
||||
[actions.CHART_UPDATE_STARTED]() {
|
||||
return Object.assign({}, state, { isChartLoading: true });
|
||||
},
|
||||
[actions.CHART_UPDATE_FAILED]() {
|
||||
return Object.assign({}, state, { isChartLoading: false });
|
||||
},
|
||||
};
|
||||
if (action.type in actionHandlers) {
|
||||
return actionHandlers[action.type]();
|
||||
|
||||
@@ -604,7 +604,7 @@ export const visTypes = {
|
||||
label: 'Heatmap',
|
||||
controlPanelSections: [
|
||||
{
|
||||
label: null,
|
||||
label: 'Axis & Metrics',
|
||||
fieldSetRows: [
|
||||
['all_columns_x'],
|
||||
['all_columns_y'],
|
||||
|
||||
Reference in New Issue
Block a user