diff --git a/docker-compose.yml b/docker-compose.yml index 5e8072c..8b9eb1d 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,7 @@ version: "3.5" services: grafana: - image: grafana/grafana:7.1.5 + image: grafana/grafana:7.4.2 ports: - "3000:3000" volumes: diff --git a/docs/remote-diagram.gif b/docs/remote-diagram.gif new file mode 100644 index 0000000..a15689a Binary files /dev/null and b/docs/remote-diagram.gif differ diff --git a/src/DiagramController.tsx b/src/DiagramController.tsx index e7ace63..9daf06e 100644 --- a/src/DiagramController.tsx +++ b/src/DiagramController.tsx @@ -1,6 +1,7 @@ import { AbsoluteTimeRange, FieldConfigSource, GrafanaTheme, InterpolateFunction, TimeZone } from '@grafana/data'; import { CustomScrollbar, LegendItem, stylesFactory } from '@grafana/ui'; import { defaultMermaidOptions } from 'config/diagramDefaults'; +import DiagramErrorBoundary from 'DiagramErrorBoundary'; import { DiagramLegend } from 'DiagramLegend'; import { css } from 'emotion'; import { merge } from 'lodash'; @@ -125,23 +126,38 @@ export class DiagramPanelController extends React.Component { + try { + const diagramId = `diagram-${this.props.id}`; + const interpolated = this.props.replaceVariables(this.contentProcessor(diagramDefinition)); + // if parsing the graph definition fails, the error handler will be called but the renderCallback() may also still be called. + this.diagramRef.innerHTML = mermaidAPI.render(diagramId, interpolated, this.renderCallback); + updateDiagramStyle(this.diagramRef, this.props.data, this.props.options, diagramId); + if (this.bindFunctions) { + this.bindFunctions(this.diagramRef); + } + } catch (err) { + this.diagramRef.innerHTML = `

Error rendering diagram. Check the diagram definition

${err}

`; } - } catch (err) { - this.diagramRef.innerHTML = `

Error rendering diagram. Check the diagram definition

${err}

`; - } + }); } } @@ -203,15 +219,17 @@ export class DiagramPanelController extends React.Component - {}} - onToggleSort={this.onToggleSort} - /> + + {}} + onToggleSort={this.onToggleSort} + /> + )} diff --git a/src/DiagramErrorBoundary.tsx b/src/DiagramErrorBoundary.tsx new file mode 100644 index 0000000..4901263 --- /dev/null +++ b/src/DiagramErrorBoundary.tsx @@ -0,0 +1,34 @@ +import React from 'react'; + +interface DiagramErrorBoundaryProps { + children: React.ReactNode; + fallback?: React.ReactNode | string; +} + +interface DiagramErrorBoundaryState { + hasError: boolean; +} + +class DiagramErrorBoundary extends React.Component { + constructor(props: any) { + super(props); + this.state = { hasError: false }; + } + + static getDerivedStateFromError(error: any) { + // Update state so the next render will show the fallback UI. + return { hasError: true }; + } + + render() { + if (this.state.hasError) { + // Render fallback + const Fallback = this.props.fallback ? this.props.fallback : 'Error rendering component'; + return
{Fallback}
; + } + + return this.props.children; + } +} + +export default DiagramErrorBoundary; diff --git a/src/config/types.ts b/src/config/types.ts index 2049829..1951cda 100644 --- a/src/config/types.ts +++ b/src/config/types.ts @@ -63,6 +63,7 @@ export interface DiagramOptions { moddedSeriesVal: number; valueName: ValueType; content: string; + contentUrl?: string; mode: DiagramPanelMode; mermaidServiceUrl: string; useBasicAuth: boolean; diff --git a/src/module.ts b/src/module.ts index e27a248..7408d21 100644 --- a/src/module.ts +++ b/src/module.ts @@ -182,6 +182,11 @@ const createPanelPlugin = () => { name: 'Use shape background for metric indicator', defaultValue: defaults.useBackground, }) + .addTextInput({ + name: 'Diagram URL', + path: 'contentUrl', + description: `Get the diagram definition from a URL`, + }) .addTextInput({ name: 'Diagram definition', path: 'content',