diff --git a/superset/assets/src/chart/Chart.jsx b/superset/assets/src/chart/Chart.jsx index a4be8dcc8..d0d346184 100644 --- a/superset/assets/src/chart/Chart.jsx +++ b/superset/assets/src/chart/Chart.jsx @@ -9,6 +9,7 @@ import RefreshChartOverlay from '../components/RefreshChartOverlay'; import StackTraceMessage from '../components/StackTraceMessage'; import ChartProps from '../visualizations/core/models/ChartProps'; import SuperChart from '../visualizations/core/components/SuperChart'; +import ErrorBoundary from '../components/ErrorBoundary'; import './chart.css'; const propTypes = { @@ -92,7 +93,7 @@ class Chart extends React.PureComponent { handleRenderFailure(e) { const { actions, chartId } = this.props; console.warn(e); // eslint-disable-line - actions.chartRenderingFailed(e, chartId); + actions.chartRenderingFailed(e.toString(), chartId); } prepareChartProps() { @@ -181,7 +182,8 @@ class Chart extends React.PureComponent { {chartAlert && ( )} @@ -194,14 +196,16 @@ class Chart extends React.PureComponent { /> )} - + + + ); } diff --git a/superset/assets/src/components/CachedLabel.jsx b/superset/assets/src/components/CachedLabel.jsx index 845eac570..f529cde3a 100644 --- a/superset/assets/src/components/CachedLabel.jsx +++ b/superset/assets/src/components/CachedLabel.jsx @@ -60,7 +60,7 @@ class CacheLabel extends React.PureComponent { onMouseOver={this.mouseOver.bind(this)} onMouseOut={this.mouseOut.bind(this)} > - cached + {t('cached')} ); } diff --git a/superset/assets/src/components/ErrorBoundary.jsx b/superset/assets/src/components/ErrorBoundary.jsx new file mode 100644 index 000000000..1be2d2e7a --- /dev/null +++ b/superset/assets/src/components/ErrorBoundary.jsx @@ -0,0 +1,45 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { t } from '@superset-ui/translation'; +import StackTraceMessage from './StackTraceMessage'; + +const propTypes = { + children: PropTypes.node.isRequired, + onError: PropTypes.func, + showMessage: PropTypes.bool, +}; +const defaultProps = { + onError: () => {}, + showMessage: true, +}; + +export default class ErrorBoundary extends React.Component { + constructor(props) { + super(props); + this.state = { error: null, info: null }; + } + + componentDidCatch(error, info) { + this.props.onError(error, info); + this.setState({ error, info }); + } + + render() { + const { error, info } = this.state; + if (error) { + const firstLine = error ? error.toString() : null; + const message = ( + + {t('Unexpected error')}{firstLine ? `: ${firstLine}` : ''} + ); + if (this.props.showMessage) { + return ( + ); + } + return null; + } + return this.props.children; + } +} +ErrorBoundary.propTypes = propTypes; +ErrorBoundary.defaultProps = defaultProps; diff --git a/superset/assets/src/components/StackTraceMessage.jsx b/superset/assets/src/components/StackTraceMessage.jsx index 9253108d2..d3a0f68be 100644 --- a/superset/assets/src/components/StackTraceMessage.jsx +++ b/superset/assets/src/components/StackTraceMessage.jsx @@ -4,12 +4,15 @@ import PropTypes from 'prop-types'; import { Alert, Collapse } from 'react-bootstrap'; const propTypes = { - message: PropTypes.string, - queryResponse: PropTypes.object, + message: PropTypes.node.isRequired, + link: PropTypes.string, + stackTrace: PropTypes.string, showStackTrace: PropTypes.bool, }; const defaultProps = { showStackTrace: false, + link: null, + stackTrace: null, }; class StackTraceMessage extends React.PureComponent { @@ -21,25 +24,17 @@ class StackTraceMessage extends React.PureComponent { }; } - hasTrace() { - return this.props.queryResponse && this.props.queryResponse.stacktrace; - } - - hasLink() { - return this.props.queryResponse && this.props.queryResponse.link; - } - render() { return ( -
+
this.setState({ showStackTrace: !this.state.showStackTrace })} > {this.props.message} - {this.hasLink() && + {this.props.link && @@ -47,10 +42,10 @@ class StackTraceMessage extends React.PureComponent { } - {this.hasTrace() && + {this.props.stackTrace &&
-              {this.props.queryResponse.stacktrace}
+              {this.props.stackTrace}
             
} diff --git a/superset/assets/src/visualizations/TimeTable/TimeTable.jsx b/superset/assets/src/visualizations/TimeTable/TimeTable.jsx index c414a7cbb..0eaafc7f6 100644 --- a/superset/assets/src/visualizations/TimeTable/TimeTable.jsx +++ b/superset/assets/src/visualizations/TimeTable/TimeTable.jsx @@ -226,7 +226,7 @@ class TimeTable extends React.PureComponent { .map(time => ({ ...data[time], time })); const reversedEntries = entries.concat().reverse(); - const defaultSort = rowType === 'column' ? { + const defaultSort = rowType === 'column' && columnConfigs.length ? { column: columnConfigs[0].key, direction: 'desc', } : false; diff --git a/superset/assets/src/visualizations/TimeTable/transformProps.js b/superset/assets/src/visualizations/TimeTable/transformProps.js index d880faeb7..d52088b99 100644 --- a/superset/assets/src/visualizations/TimeTable/transformProps.js +++ b/superset/assets/src/visualizations/TimeTable/transformProps.js @@ -1,7 +1,7 @@ export default function transformProps(chartProps) { const { height, datasource, formData, payload } = chartProps; const { - columnCollection, + columnCollection = 0, groupby, metrics, url,