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,