mirror of
https://github.com/apache/superset.git
synced 2026-04-21 09:04:38 +00:00
in explore view, controls like color cheme, legend, rich tooltip, etc., change these controls should see effect instantly, without click Run Query.
234 lines
7.1 KiB
JavaScript
234 lines
7.1 KiB
JavaScript
/* eslint camelcase: 0 */
|
|
import React from 'react';
|
|
import PropTypes from 'prop-types';
|
|
import { bindActionCreators } from 'redux';
|
|
import { connect } from 'react-redux';
|
|
|
|
import ExploreChartPanel from './ExploreChartPanel';
|
|
import ControlPanelsContainer from './ControlPanelsContainer';
|
|
import SaveModal from './SaveModal';
|
|
import QueryAndSaveBtns from './QueryAndSaveBtns';
|
|
import { getExploreUrl } from '../exploreUtils';
|
|
import { areObjectsEqual } from '../../reduxUtils';
|
|
import { getFormDataFromControls } from '../stores/store';
|
|
import { chartPropType } from '../../chart/chartReducer';
|
|
import * as exploreActions from '../actions/exploreActions';
|
|
import * as saveModalActions from '../actions/saveModalActions';
|
|
import * as chartActions from '../../chart/chartAction';
|
|
|
|
const propTypes = {
|
|
actions: PropTypes.object.isRequired,
|
|
datasource_type: PropTypes.string.isRequired,
|
|
isDatasourceMetaLoading: PropTypes.bool.isRequired,
|
|
chartStatus: PropTypes.string,
|
|
chart: PropTypes.shape(chartPropType).isRequired,
|
|
controls: PropTypes.object.isRequired,
|
|
forcedHeight: PropTypes.string,
|
|
form_data: PropTypes.object.isRequired,
|
|
standalone: PropTypes.bool.isRequired,
|
|
timeout: PropTypes.number,
|
|
};
|
|
|
|
class ExploreViewContainer extends React.Component {
|
|
constructor(props) {
|
|
super(props);
|
|
this.state = {
|
|
height: this.getHeight(),
|
|
width: this.getWidth(),
|
|
showModal: false,
|
|
};
|
|
}
|
|
|
|
componentDidMount() {
|
|
window.addEventListener('resize', this.handleResize.bind(this));
|
|
}
|
|
|
|
componentWillReceiveProps(np) {
|
|
if (np.controls.viz_type.value !== this.props.controls.viz_type.value) {
|
|
this.props.actions.resetControls();
|
|
this.props.actions.triggerQuery(true, this.props.chart.chartKey);
|
|
}
|
|
if (np.controls.datasource.value !== this.props.controls.datasource.value) {
|
|
this.props.actions.fetchDatasourceMetadata(np.form_data.datasource, true);
|
|
}
|
|
// if any control value changed and it's an instant control
|
|
if (Object.keys(np.controls).some(key => (np.controls[key].renderTrigger &&
|
|
!areObjectsEqual(np.controls[key].value, this.props.controls[key].value)))) {
|
|
this.props.actions.renderTriggered(new Date().getTime(), this.props.chart.chartKey);
|
|
}
|
|
}
|
|
|
|
componentDidUpdate() {
|
|
this.triggerQueryIfNeeded();
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
window.removeEventListener('resize', this.handleResize.bind(this));
|
|
}
|
|
|
|
onQuery() {
|
|
// remove alerts when query
|
|
this.props.actions.removeControlPanelAlert();
|
|
this.props.actions.triggerQuery(true, this.props.chart.chartKey);
|
|
|
|
history.pushState(
|
|
{},
|
|
document.title,
|
|
getExploreUrl(this.props.form_data));
|
|
}
|
|
|
|
onStop() {
|
|
this.props.actions.chartUpdateStopped(this.props.chart.queryRequest);
|
|
}
|
|
|
|
getWidth() {
|
|
return `${window.innerWidth}px`;
|
|
}
|
|
|
|
getHeight() {
|
|
if (this.props.forcedHeight) {
|
|
return this.props.forcedHeight + 'px';
|
|
}
|
|
const navHeight = this.props.standalone ? 0 : 90;
|
|
return `${window.innerHeight - navHeight}px`;
|
|
}
|
|
|
|
triggerQueryIfNeeded() {
|
|
if (this.props.chart.triggerQuery && !this.hasErrors()) {
|
|
this.props.actions.runQuery(this.props.form_data, false,
|
|
this.props.timeout, this.props.chart.chartKey);
|
|
}
|
|
}
|
|
|
|
handleResize() {
|
|
clearTimeout(this.resizeTimer);
|
|
this.resizeTimer = setTimeout(() => {
|
|
this.setState({ height: this.getHeight(), width: this.getWidth() });
|
|
}, 250);
|
|
}
|
|
|
|
toggleModal() {
|
|
this.setState({ showModal: !this.state.showModal });
|
|
}
|
|
hasErrors() {
|
|
const ctrls = this.props.controls;
|
|
return Object.keys(ctrls).some(
|
|
k => ctrls[k].validationErrors && ctrls[k].validationErrors.length > 0);
|
|
}
|
|
renderErrorMessage() {
|
|
// Returns an error message as a node if any errors are in the store
|
|
const errors = [];
|
|
for (const controlName in this.props.controls) {
|
|
const control = this.props.controls[controlName];
|
|
if (control.validationErrors && control.validationErrors.length > 0) {
|
|
errors.push(
|
|
<div key={controlName}>
|
|
<strong>{`[ ${control.label} ] `}</strong>
|
|
{control.validationErrors.join('. ')}
|
|
</div>,
|
|
);
|
|
}
|
|
}
|
|
let errorMessage;
|
|
if (errors.length > 0) {
|
|
errorMessage = (
|
|
<div style={{ textAlign: 'left' }}>{errors}</div>
|
|
);
|
|
}
|
|
return errorMessage;
|
|
}
|
|
renderChartContainer() {
|
|
return (
|
|
<ExploreChartPanel
|
|
width={this.state.width}
|
|
height={this.state.height}
|
|
{...this.props}
|
|
/>);
|
|
}
|
|
|
|
render() {
|
|
if (this.props.standalone) {
|
|
return this.renderChartContainer();
|
|
}
|
|
return (
|
|
<div
|
|
id="explore-container"
|
|
className="container-fluid"
|
|
style={{
|
|
height: this.state.height,
|
|
overflow: 'hidden',
|
|
}}
|
|
>
|
|
{this.state.showModal &&
|
|
<SaveModal
|
|
onHide={this.toggleModal.bind(this)}
|
|
actions={this.props.actions}
|
|
form_data={this.props.form_data}
|
|
/>
|
|
}
|
|
<div className="row">
|
|
<div className="col-sm-4">
|
|
<QueryAndSaveBtns
|
|
canAdd="True"
|
|
onQuery={this.onQuery.bind(this)}
|
|
onSave={this.toggleModal.bind(this)}
|
|
onStop={this.onStop.bind(this)}
|
|
loading={this.props.chart.chartStatus === 'loading'}
|
|
errorMessage={this.renderErrorMessage()}
|
|
/>
|
|
<br />
|
|
<ControlPanelsContainer
|
|
actions={this.props.actions}
|
|
form_data={this.props.form_data}
|
|
controls={this.props.controls}
|
|
datasource_type={this.props.datasource_type}
|
|
isDatasourceMetaLoading={this.props.isDatasourceMetaLoading}
|
|
/>
|
|
</div>
|
|
<div className="col-sm-8">
|
|
{this.renderChartContainer()}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
}
|
|
|
|
ExploreViewContainer.propTypes = propTypes;
|
|
|
|
function mapStateToProps({ explore, charts }) {
|
|
const form_data = getFormDataFromControls(explore.controls);
|
|
const chartKey = Object.keys(charts)[0];
|
|
const chart = charts[chartKey];
|
|
return {
|
|
isDatasourceMetaLoading: explore.isDatasourceMetaLoading,
|
|
datasource: explore.datasource,
|
|
datasource_type: explore.datasource.type,
|
|
datasourceId: explore.datasource_id,
|
|
controls: explore.controls,
|
|
can_overwrite: !!explore.can_overwrite,
|
|
can_download: !!explore.can_download,
|
|
column_formats: explore.datasource ? explore.datasource.column_formats : null,
|
|
containerId: explore.slice ? `slice-container-${explore.slice.slice_id}` : 'slice-container',
|
|
isStarred: explore.isStarred,
|
|
slice: explore.slice,
|
|
form_data,
|
|
table_name: form_data.datasource_name,
|
|
vizType: form_data.viz_type,
|
|
standalone: explore.standalone,
|
|
forcedHeight: explore.forced_height,
|
|
chart,
|
|
timeout: explore.common.conf.SUPERSET_WEBSERVER_TIMEOUT,
|
|
};
|
|
}
|
|
|
|
function mapDispatchToProps(dispatch) {
|
|
const actions = Object.assign({}, exploreActions, saveModalActions, chartActions);
|
|
return {
|
|
actions: bindActionCreators(actions, dispatch),
|
|
};
|
|
}
|
|
|
|
export { ExploreViewContainer };
|
|
export default connect(mapStateToProps, mapDispatchToProps)(ExploreViewContainer);
|