diff --git a/src/core_plugins/kibana/public/dashboard/panel/dashboard_panel.js b/src/core_plugins/kibana/public/dashboard/panel/dashboard_panel.js
index 76c6b7a494cd3..1243bdf2ac90c 100644
--- a/src/core_plugins/kibana/public/dashboard/panel/dashboard_panel.js
+++ b/src/core_plugins/kibana/public/dashboard/panel/dashboard_panel.js
@@ -4,6 +4,7 @@ import classNames from 'classnames';
import { DashboardViewMode } from '../dashboard_view_mode';
import { PanelHeader } from './panel_header';
+import { PanelError } from './panel_error';
export class DashboardPanel extends React.Component {
@@ -15,13 +16,18 @@ export class DashboardPanel extends React.Component {
this._isMounted = false;
}
- async componentDidMount() {
+ componentDidMount() {
this._isMounted = true;
const { getEmbeddableHandler, panel, getContainerApi } = this.props;
this.containerApi = getContainerApi();
this.embeddableHandler = getEmbeddableHandler(panel.type);
+ if (!this.embeddableHandler) {
+ /* eslint-disable react/no-did-mount-set-state */
+ this.setState({ error: `Invalid panel type ${panel.type}` });
+ }
+
// TODO: use redux instead of the isMounted anti-pattern to handle the case when the component is unmounted
// before the async calls above return. We can then get rid of the eslint disable line. Without redux, there is
// not a better option, since you aren't supposed to run async calls inside of componentWillMount.
@@ -37,10 +43,15 @@ export class DashboardPanel extends React.Component {
});
if (this._isMounted) {
- this.destroyEmbeddable = await this.embeddableHandler.render(
- this.panelElement,
- panel,
- this.containerApi);
+ this.embeddableHandler.render(
+ this.panelElement,
+ panel,
+ this.containerApi)
+ .then(destroyEmbeddable => this.destroyEmbeddable = destroyEmbeddable)
+ .catch(error => {
+ const message = error.message || JSON.stringify(error);
+ this.setState({ error: message });
+ });
}
}
@@ -78,6 +89,20 @@ export class DashboardPanel extends React.Component {
}
}
+ renderEmbeddedContent() {
+ return (
+
this.panelElement = panelElement}
+ />
+ );
+ }
+
+ renderEmbeddedError() {
+ return
;
+ }
+
render() {
const { title } = this.state;
const { dashboardViewMode, isFullScreenMode, isExpanded } = this.props;
@@ -102,11 +127,9 @@ export class DashboardPanel extends React.Component {
isExpanded={isExpanded}
isViewOnlyMode={isFullScreenMode || dashboardViewMode === DashboardViewMode.VIEW}
/>
-
this.panelElement = panelElement}
- />
+
+ {this.state.error ? this.renderEmbeddedError() : this.renderEmbeddedContent()}
+
);
diff --git a/src/core_plugins/kibana/public/dashboard/panel/dashboard_panel.test.js b/src/core_plugins/kibana/public/dashboard/panel/dashboard_panel.test.js
index 3cc6b99b3a0e6..26d56fa50d5d0 100644
--- a/src/core_plugins/kibana/public/dashboard/panel/dashboard_panel.test.js
+++ b/src/core_plugins/kibana/public/dashboard/panel/dashboard_panel.test.js
@@ -3,6 +3,7 @@ import _ from 'lodash';
import { mount } from 'enzyme';
import { DashboardViewMode } from '../dashboard_view_mode';
import { DashboardPanel } from './dashboard_panel';
+import { PanelError } from '../panel/panel_error';
const containerApiMock = {
addFilter: () => {},
@@ -15,7 +16,7 @@ const containerApiMock = {
const embeddableHandlerMock = {
getEditPath: () => Promise.resolve('editPath'),
getTitleFor: () => Promise.resolve('title'),
- render: jest.fn()
+ render: jest.fn(() => Promise.resolve(() => {}))
};
function getProps(props = {}) {
@@ -45,3 +46,24 @@ test('DashboardPanel matches snapshot', () => {
test('and calls render', () => {
expect(embeddableHandlerMock.render.mock.calls.length).toBe(1);
});
+
+test('renders an error message when an error is thrown', () => {
+ const props = getProps({
+ getEmbeddableHandler: () => {
+ return {
+ getEditPath: () => Promise.resolve('editPath'),
+ getTitleFor: () => Promise.resolve('title'),
+ render: () => Promise.reject(new Error({ message: 'simulated error' }))
+ };
+ }
+ });
+ const component = mount();
+ return new Promise(resolve => {
+ return process.nextTick(() => {
+ const panelElements = component.find(PanelError);
+ expect(panelElements.length).toBe(1);
+ resolve();
+ });
+ });
+});
+
diff --git a/src/core_plugins/kibana/public/dashboard/panel/panel_error.js b/src/core_plugins/kibana/public/dashboard/panel/panel_error.js
new file mode 100644
index 0000000000000..6c247691496ff
--- /dev/null
+++ b/src/core_plugins/kibana/public/dashboard/panel/panel_error.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+
+export function PanelError({ error }) {
+ return (
+
+
+ {error}
+
+ );
+}
+
+PanelError.PropTypes = {
+ error: PropTypes.oneOfType([
+ PropTypes.string,
+ PropTypes.node
+ ]),
+};
+