Move data links suggestions to grafana-data

This commit is contained in:
Dominik Prokop 2020-02-10 17:00:21 +01:00
parent cd6809685f
commit 5d8d01a65e
24 changed files with 278 additions and 272 deletions

View File

@ -7,3 +7,4 @@ export * from './datetime';
export * from './text';
export * from './valueFormats';
export * from './field';
export * from './links';

View File

@ -0,0 +1 @@
export * from './variableSuggestions';

View File

@ -0,0 +1,232 @@
import { DataFrame, Field, FieldType } from '../types/dataFrame';
import { KeyValue } from '../types/data';
import { chain } from 'lodash';
export enum VariableOrigin {
Series = 'series',
Field = 'field',
Fields = 'fields',
Value = 'value',
BuiltIn = 'built-in',
Template = 'template',
}
export interface VariableSuggestion {
value: string;
label: string;
documentation?: string;
origin: VariableOrigin;
}
export const DataLinkBuiltInVars = {
keepTime: '__url_time_range',
timeRangeFrom: '__from',
timeRangeTo: '__to',
includeVars: '__all_variables',
seriesName: '__series.name',
fieldName: '__field.name',
valueTime: '__value.time',
valueNumeric: '__value.numeric',
valueText: '__value.text',
valueRaw: '__value.raw',
// name of the calculation represented by the value
valueCalc: '__value.calc',
};
const timeRangeVars = [
{
value: `${DataLinkBuiltInVars.keepTime}`,
label: 'Time range',
documentation: 'Adds current time range',
origin: VariableOrigin.BuiltIn,
},
{
value: `${DataLinkBuiltInVars.timeRangeFrom}`,
label: 'Time range: from',
documentation: "Adds current time range's from value",
origin: VariableOrigin.BuiltIn,
},
{
value: `${DataLinkBuiltInVars.timeRangeTo}`,
label: 'Time range: to',
documentation: "Adds current time range's to value",
origin: VariableOrigin.BuiltIn,
},
];
const seriesVars = [
{
value: `${DataLinkBuiltInVars.seriesName}`,
label: 'Name',
documentation: 'Name of the series',
origin: VariableOrigin.Series,
},
];
const valueVars = [
{
value: `${DataLinkBuiltInVars.valueNumeric}`,
label: 'Numeric',
documentation: 'Numeric representation of selected value',
origin: VariableOrigin.Value,
},
{
value: `${DataLinkBuiltInVars.valueText}`,
label: 'Text',
documentation: 'Text representation of selected value',
origin: VariableOrigin.Value,
},
{
value: `${DataLinkBuiltInVars.valueRaw}`,
label: 'Raw',
documentation: 'Raw value',
origin: VariableOrigin.Value,
},
];
const buildLabelPath = (label: string) => {
return label.indexOf('.') > -1 ? `["${label}"]` : `.${label}`;
};
let templateSrvInstance: {
variables: Array<{ name: string }>;
};
export const getPanelLinksVariableSuggestions = (): VariableSuggestion[] => [
...templateSrvInstance.variables.map(variable => ({
value: variable.name as string,
label: variable.name,
origin: VariableOrigin.Template,
})),
{
value: `${DataLinkBuiltInVars.includeVars}`,
label: 'All variables',
documentation: 'Adds current variables',
origin: VariableOrigin.Template,
},
...timeRangeVars,
];
const getFieldVars = (dataFrames: DataFrame[]) => {
const all = [];
for (const df of dataFrames) {
for (const f of df.fields) {
if (f.labels) {
for (const k of Object.keys(f.labels)) {
all.push(k);
}
}
}
}
const labels = chain(all)
.flatten()
.uniq()
.value();
return [
{
value: `${DataLinkBuiltInVars.fieldName}`,
label: 'Name',
documentation: 'Field name of the clicked datapoint (in ms epoch)',
origin: VariableOrigin.Field,
},
...labels.map(label => ({
value: `__field.labels${buildLabelPath(label)}`,
label: `labels.${label}`,
documentation: `${label} label value`,
origin: VariableOrigin.Field,
})),
];
};
const getDataFrameVars = (dataFrames: DataFrame[]) => {
// @ts-ignore
let numeric: Field = undefined;
// @ts-ignore
let title: Field = undefined;
const suggestions: VariableSuggestion[] = [];
const keys: KeyValue<true> = {};
for (const df of dataFrames) {
for (const f of df.fields) {
if (keys[f.name]) {
continue;
}
suggestions.push({
value: `__data.fields[${f.name}]`,
label: `${f.name}`,
documentation: `Formatted value for ${f.name} on the same row`,
origin: VariableOrigin.Fields,
});
keys[f.name] = true;
if (!numeric && f.type === FieldType.number) {
numeric = f;
}
if (!title && f.config.title && f.config.title !== f.name) {
title = f;
}
}
}
if (suggestions.length) {
suggestions.push({
value: `__data.fields[0]`,
label: `Select by index`,
documentation: `Enter the field order`,
origin: VariableOrigin.Fields,
});
}
if (numeric) {
suggestions.push({
value: `__data.fields[${numeric.name}].numeric`,
label: `Show numeric value`,
documentation: `the numeric field value`,
origin: VariableOrigin.Fields,
});
suggestions.push({
value: `__data.fields[${numeric.name}].text`,
label: `Show text value`,
documentation: `the text value`,
origin: VariableOrigin.Fields,
});
}
if (title) {
suggestions.push({
value: `__data.fields[${title.config.title}]`,
label: `Select by title`,
documentation: `Use the title to pick the field`,
origin: VariableOrigin.Fields,
});
}
return suggestions;
};
export const getDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => {
const valueTimeVar = {
value: `${DataLinkBuiltInVars.valueTime}`,
label: 'Time',
documentation: 'Time value of the clicked datapoint (in ms epoch)',
origin: VariableOrigin.Value,
};
return [
...seriesVars,
...getFieldVars(dataFrames),
...valueVars,
valueTimeVar,
...getDataFrameVars(dataFrames),
...getPanelLinksVariableSuggestions(),
];
};
export const getCalculationValueDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => {
const fieldVars = getFieldVars(dataFrames);
const valueCalcVar = {
value: `${DataLinkBuiltInVars.valueCalc}`,
label: 'Calculation name',
documentation: 'Name of the calculation the value is a result of',
origin: VariableOrigin.Value,
};
return [...seriesVars, ...fieldVars, ...valueVars, valueCalcVar, ...getPanelLinksVariableSuggestions()];
};
export const initialiseVariableSuggestions = (templateSrv: any) => (templateSrvInstance = templateSrv);

View File

@ -1,11 +1,10 @@
import React, { ChangeEvent, useContext } from 'react';
import { DataLink } from '@grafana/data';
import { FormField, Switch } from '../index';
import { VariableSuggestion } from './DataLinkSuggestions';
import { css } from 'emotion';
import { ThemeContext, stylesFactory } from '../../themes/index';
import { DataLinkInput } from './DataLinkInput';
import { GrafanaTheme } from '@grafana/data';
import { GrafanaTheme, VariableSuggestion } from '@grafana/data';
interface DataLinkEditorProps {
index: number;

View File

@ -1,7 +1,7 @@
import React, { useState, useMemo, useContext, useRef, RefObject, memo, useEffect } from 'react';
import usePrevious from 'react-use/lib/usePrevious';
import { VariableSuggestion, VariableOrigin, DataLinkSuggestions } from './DataLinkSuggestions';
import { ThemeContext, DataLinkBuiltInVars, makeValue } from '../../index';
import { DataLinkSuggestions } from './DataLinkSuggestions';
import { ThemeContext, makeValue } from '../../index';
import { SelectionReference } from './SelectionReference';
import { Portal } from '../index';
@ -14,7 +14,7 @@ import { css, cx } from 'emotion';
import { SlatePrism } from '../../slate-plugins';
import { SCHEMA } from '../../utils/slate';
import { stylesFactory } from '../../themes';
import { GrafanaTheme } from '@grafana/data';
import { GrafanaTheme, VariableSuggestion, VariableOrigin, DataLinkBuiltInVars } from '@grafana/data';
const modulo = (a: number, n: number) => a - n * Math.floor(a / n);

View File

@ -1,5 +1,5 @@
import { selectThemeVariant, ThemeContext } from '../../index';
import { GrafanaTheme } from '@grafana/data';
import { GrafanaTheme, VariableSuggestion } from '@grafana/data';
import { css, cx } from 'emotion';
import _ from 'lodash';
import React, { useRef, useContext, useMemo } from 'react';
@ -8,22 +8,6 @@ import { List } from '../index';
import tinycolor from 'tinycolor2';
import { stylesFactory } from '../../themes';
export enum VariableOrigin {
Series = 'series',
Field = 'field',
Fields = 'fields',
Value = 'value',
BuiltIn = 'built-in',
Template = 'template',
}
export interface VariableSuggestion {
value: string;
label: string;
documentation?: string;
origin: VariableOrigin;
}
interface DataLinkSuggestionsProps {
suggestions: VariableSuggestion[];
activeIndex: number;

View File

@ -4,10 +4,9 @@ import React, { FC } from 'react';
import Prism from 'prismjs';
// Components
import { css } from 'emotion';
import { DataLink } from '@grafana/data';
import { DataLink, VariableSuggestion } from '@grafana/data';
import { Button } from '../index';
import { DataLinkEditor } from './DataLinkEditor';
import { VariableSuggestion } from './DataLinkSuggestions';
import { useTheme } from '../../themes/ThemeContext';
interface DataLinksEditorProps {

View File

@ -95,7 +95,6 @@ export { ClickOutsideWrapper } from './ClickOutsideWrapper/ClickOutsideWrapper';
export * from './SingleStatShared/index';
export { CallToActionCard } from './CallToActionCard/CallToActionCard';
export { ContextMenu, ContextMenuItem, ContextMenuGroup, ContextMenuProps } from './ContextMenu/ContextMenu';
export { VariableSuggestion, VariableOrigin } from './DataLinks/DataLinkSuggestions';
export { DataLinksEditor } from './DataLinks/DataLinksEditor';
export { DataLinkInput } from './DataLinks/DataLinkInput';
export { DataLinksContextMenu } from './DataLinks/DataLinksContextMenu';

View File

@ -1,21 +1,6 @@
import { ContextMenuItem } from '../components/ContextMenu/ContextMenu';
import { LinkModelSupplier } from '@grafana/data';
export const DataLinkBuiltInVars = {
keepTime: '__url_time_range',
timeRangeFrom: '__from',
timeRangeTo: '__to',
includeVars: '__all_variables',
seriesName: '__series.name',
fieldName: '__field.name',
valueTime: '__value.time',
valueNumeric: '__value.numeric',
valueText: '__value.text',
valueRaw: '__value.raw',
// name of the calculation represented by the value
valueCalc: '__value.calc',
};
/**
* Delays creating links until we need to open the ContextMenu
*/

View File

@ -8,9 +8,8 @@ import './../../panel/GeneralTabCtrl';
// Types
import { PanelModel } from '../state/PanelModel';
import { DataLink } from '@grafana/data';
import { DataLink, getPanelLinksVariableSuggestions } from '@grafana/data';
import { PanelOptionsGroup, DataLinksEditor } from '@grafana/ui';
import { getPanelLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';
interface Props {
panel: PanelModel;

View File

@ -3,7 +3,7 @@ import { DashboardModel } from '../state/DashboardModel';
import { PanelModel } from '../state/PanelModel';
import { GRID_CELL_HEIGHT, GRID_CELL_VMARGIN } from 'app/core/constants';
import { expect } from 'test/lib/common';
import { DataLinkBuiltInVars } from '@grafana/ui';
import { DataLinkBuiltInVars } from '@grafana/data';
jest.mock('app/core/services/context_srv', () => ({}));

View File

@ -9,7 +9,7 @@ import kbn from 'app/core/utils/kbn';
// Types
import { PanelModel } from './PanelModel';
import { DashboardModel } from './DashboardModel';
import { DataLink } from '@grafana/data';
import { DataLink, DataLinkBuiltInVars } from '@grafana/data';
// Constants
import {
@ -20,7 +20,6 @@ import {
MIN_PANEL_HEIGHT,
DEFAULT_PANEL_SPAN,
} from 'app/core/constants';
import { DataLinkBuiltInVars } from '@grafana/ui';
export class DashboardMigrator {
dashboard: DashboardModel;

View File

@ -1,211 +1,11 @@
import _ from 'lodash';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import templateSrv, { TemplateSrv } from 'app/features/templating/template_srv';
import { TemplateSrv } from 'app/features/templating/template_srv';
import coreModule from 'app/core/core_module';
import { appendQueryToUrl, toUrlParams } from 'app/core/utils/url';
import { sanitizeUrl } from 'app/core/utils/text';
import { getConfig } from 'app/core/config';
import { VariableSuggestion, VariableOrigin, DataLinkBuiltInVars } from '@grafana/ui';
import {
DataLink,
KeyValue,
deprecationWarning,
LinkModel,
DataFrame,
ScopedVars,
FieldType,
Field,
} from '@grafana/data';
const timeRangeVars = [
{
value: `${DataLinkBuiltInVars.keepTime}`,
label: 'Time range',
documentation: 'Adds current time range',
origin: VariableOrigin.BuiltIn,
},
{
value: `${DataLinkBuiltInVars.timeRangeFrom}`,
label: 'Time range: from',
documentation: "Adds current time range's from value",
origin: VariableOrigin.BuiltIn,
},
{
value: `${DataLinkBuiltInVars.timeRangeTo}`,
label: 'Time range: to',
documentation: "Adds current time range's to value",
origin: VariableOrigin.BuiltIn,
},
];
const seriesVars = [
{
value: `${DataLinkBuiltInVars.seriesName}`,
label: 'Name',
documentation: 'Name of the series',
origin: VariableOrigin.Series,
},
];
const valueVars = [
{
value: `${DataLinkBuiltInVars.valueNumeric}`,
label: 'Numeric',
documentation: 'Numeric representation of selected value',
origin: VariableOrigin.Value,
},
{
value: `${DataLinkBuiltInVars.valueText}`,
label: 'Text',
documentation: 'Text representation of selected value',
origin: VariableOrigin.Value,
},
{
value: `${DataLinkBuiltInVars.valueRaw}`,
label: 'Raw',
documentation: 'Raw value',
origin: VariableOrigin.Value,
},
];
const buildLabelPath = (label: string) => {
return label.indexOf('.') > -1 ? `["${label}"]` : `.${label}`;
};
export const getPanelLinksVariableSuggestions = (): VariableSuggestion[] => [
...templateSrv.variables.map(variable => ({
value: variable.name as string,
label: variable.name,
origin: VariableOrigin.Template,
})),
{
value: `${DataLinkBuiltInVars.includeVars}`,
label: 'All variables',
documentation: 'Adds current variables',
origin: VariableOrigin.Template,
},
...timeRangeVars,
];
const getFieldVars = (dataFrames: DataFrame[]) => {
const all = [];
for (const df of dataFrames) {
for (const f of df.fields) {
if (f.labels) {
for (const k of Object.keys(f.labels)) {
all.push(k);
}
}
}
}
const labels = _.chain(all)
.flatten()
.uniq()
.value();
return [
{
value: `${DataLinkBuiltInVars.fieldName}`,
label: 'Name',
documentation: 'Field name of the clicked datapoint (in ms epoch)',
origin: VariableOrigin.Field,
},
...labels.map(label => ({
value: `__field.labels${buildLabelPath(label)}`,
label: `labels.${label}`,
documentation: `${label} label value`,
origin: VariableOrigin.Field,
})),
];
};
const getDataFrameVars = (dataFrames: DataFrame[]) => {
let numeric: Field = undefined;
let title: Field = undefined;
const suggestions: VariableSuggestion[] = [];
const keys: KeyValue<true> = {};
for (const df of dataFrames) {
for (const f of df.fields) {
if (keys[f.name]) {
continue;
}
suggestions.push({
value: `__data.fields[${f.name}]`,
label: `${f.name}`,
documentation: `Formatted value for ${f.name} on the same row`,
origin: VariableOrigin.Fields,
});
keys[f.name] = true;
if (!numeric && f.type === FieldType.number) {
numeric = f;
}
if (!title && f.config.title && f.config.title !== f.name) {
title = f;
}
}
}
if (suggestions.length) {
suggestions.push({
value: `__data.fields[0]`,
label: `Select by index`,
documentation: `Enter the field order`,
origin: VariableOrigin.Fields,
});
}
if (numeric) {
suggestions.push({
value: `__data.fields[${numeric.name}].numeric`,
label: `Show numeric value`,
documentation: `the numeric field value`,
origin: VariableOrigin.Fields,
});
suggestions.push({
value: `__data.fields[${numeric.name}].text`,
label: `Show text value`,
documentation: `the text value`,
origin: VariableOrigin.Fields,
});
}
if (title) {
suggestions.push({
value: `__data.fields[${title.config.title}]`,
label: `Select by title`,
documentation: `Use the title to pick the field`,
origin: VariableOrigin.Fields,
});
}
return suggestions;
};
export const getDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => {
const valueTimeVar = {
value: `${DataLinkBuiltInVars.valueTime}`,
label: 'Time',
documentation: 'Time value of the clicked datapoint (in ms epoch)',
origin: VariableOrigin.Value,
};
return [
...seriesVars,
...getFieldVars(dataFrames),
...valueVars,
valueTimeVar,
...getDataFrameVars(dataFrames),
...getPanelLinksVariableSuggestions(),
];
};
export const getCalculationValueDataLinksVariableSuggestions = (dataFrames: DataFrame[]): VariableSuggestion[] => {
const fieldVars = getFieldVars(dataFrames);
const valueCalcVar = {
value: `${DataLinkBuiltInVars.valueCalc}`,
label: 'Calculation name',
documentation: 'Name of the calculation the value is a result of',
origin: VariableOrigin.Value,
};
return [...seriesVars, ...fieldVars, ...valueVars, valueCalcVar, ...getPanelLinksVariableSuggestions()];
};
import { DataLink, KeyValue, deprecationWarning, LinkModel, ScopedVars, DataLinkBuiltInVars } from '@grafana/data';
export interface LinkService {
getDataLinkUIModel: <T>(link: DataLink, scopedVars: ScopedVars, origin: T) => LinkModel<T>;

View File

@ -1,5 +1,5 @@
import { LinkSrv } from '../link_srv';
import { DataLinkBuiltInVars } from '@grafana/ui';
import { DataLinkBuiltInVars, initialiseVariableSuggestions } from '@grafana/data';
import _ from 'lodash';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { TemplateSrv } from 'app/features/templating/template_srv';
@ -71,6 +71,8 @@ describe('linkSrv', () => {
},
]);
initialiseVariableSuggestions(_templateSrv);
linkSrv = new LinkSrv(_templateSrv, timeSrv);
}

View File

@ -1,6 +1,8 @@
import React from 'react';
import { css } from 'emotion';
import { Button, FormField, VariableSuggestion, DataLinkInput, stylesFactory } from '@grafana/ui';
import { Button, FormField, DataLinkInput, stylesFactory } from '@grafana/ui';
import { VariableSuggestion } from '@grafana/data';
import { DataLinkConfig } from '../types';
const getStyles = stylesFactory(() => ({

View File

@ -1,7 +1,7 @@
import React from 'react';
import { css } from 'emotion';
import { Button, DataLinkBuiltInVars, stylesFactory, useTheme, VariableOrigin } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data';
import { Button, stylesFactory, useTheme } from '@grafana/ui';
import { GrafanaTheme, VariableOrigin, DataLinkBuiltInVars } from '@grafana/data';
import { DataLinkConfig } from '../types';
import { DataLink } from './DataLink';

View File

@ -1,6 +1,7 @@
import React from 'react';
import { css } from 'emotion';
import { Button, FormField, VariableSuggestion, DataLinkInput, stylesFactory } from '@grafana/ui';
import { VariableSuggestion } from '@grafana/data';
import { Button, FormField, DataLinkInput, stylesFactory } from '@grafana/ui';
import { DerivedFieldConfig } from '../types';
const getStyles = stylesFactory(() => ({

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { css } from 'emotion';
import { Button, DataLinkBuiltInVars, stylesFactory, useTheme, VariableOrigin } from '@grafana/ui';
import { GrafanaTheme } from '@grafana/data';
import { Button, stylesFactory, useTheme } from '@grafana/ui';
import { GrafanaTheme, DataLinkBuiltInVars, VariableOrigin } from '@grafana/data';
import { DerivedFieldConfig } from '../types';
import { DerivedField } from './DerivedField';
import { DebugSection } from './DebugSection';

View File

@ -20,13 +20,11 @@ import {
FieldConfig,
DataLink,
PanelEditorProps,
getDataLinksVariableSuggestions,
getCalculationValueDataLinksVariableSuggestions,
} from '@grafana/data';
import { BarGaugeOptions, displayModes } from './types';
import { orientationOptions } from '../gauge/types';
import {
getDataLinksVariableSuggestions,
getCalculationValueDataLinksVariableSuggestions,
} from 'app/features/panel/panellinks/link_srv';
import { config } from 'app/core/config';
export class BarGaugePanelEditor extends PureComponent<PanelEditorProps<BarGaugeOptions>> {

View File

@ -17,13 +17,11 @@ import {
ValueMapping,
FieldConfig,
DataLink,
getCalculationValueDataLinksVariableSuggestions,
getDataLinksVariableSuggestions,
} from '@grafana/data';
import { GaugeOptions } from './types';
import {
getCalculationValueDataLinksVariableSuggestions,
getDataLinksVariableSuggestions,
} from 'app/features/panel/panellinks/link_srv';
import { config } from 'app/core/config';
export class GaugePanelEditor extends PureComponent<PanelEditorProps<GaugeOptions>> {

View File

@ -11,12 +11,18 @@ import { DataProcessor } from './data_processor';
import { axesEditorComponent } from './axes_editor';
import config from 'app/core/config';
import TimeSeries from 'app/core/time_series2';
import { VariableSuggestion } from '@grafana/ui';
import { getProcessedDataFrames } from 'app/features/dashboard/state/runRequest';
import { getColorFromHexRgbOrName, PanelEvents, DataFrame, DataLink, DateTimeInput } from '@grafana/data';
import {
getColorFromHexRgbOrName,
PanelEvents,
DataFrame,
DataLink,
DateTimeInput,
VariableSuggestion,
getDataLinksVariableSuggestions,
} from '@grafana/data';
import { GraphContextMenuCtrl } from './GraphContextMenuCtrl';
import { getDataLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';
import { auto } from 'angular';
import { AnnotationsSrv } from 'app/features/annotations/all';

View File

@ -1,5 +1,5 @@
import { GraphCtrl } from '../module';
import { dateTime } from '@grafana/data';
import { dateTime, initialiseVariableSuggestions } from '@grafana/data';
import TimeSeries from 'app/core/time_series2';
jest.mock('../graph', () => ({}));
@ -44,6 +44,9 @@ describe('GraphCtrl', () => {
};
ctx.ctrl.annotationsPromise = Promise.resolve({});
ctx.ctrl.updateTimeRange();
initialiseVariableSuggestions({
variables: [],
});
});
describe('when time series are outside range', () => {

View File

@ -20,15 +20,13 @@ import {
DataLink,
PanelEditorProps,
FieldDisplayOptions,
getDataLinksVariableSuggestions,
getCalculationValueDataLinksVariableSuggestions,
} from '@grafana/data';
import { StatPanelOptions, colorModes, graphModes, justifyModes } from './types';
import { orientationOptions } from '../gauge/types';
import {
getDataLinksVariableSuggestions,
getCalculationValueDataLinksVariableSuggestions,
} from 'app/features/panel/panellinks/link_srv';
import { config } from 'app/core/config';
export class StatPanelEditor extends PureComponent<PanelEditorProps<StatPanelOptions>> {

View File

@ -10,6 +10,7 @@ import { ContextSrv } from 'app/core/services/context_srv';
import { provideTheme } from 'app/core/utils/ConfigProvider';
import { ErrorBoundaryAlert } from '@grafana/ui';
import { GrafanaRootScope } from './GrafanaCtrl';
import { initialiseVariableSuggestions } from '@grafana/data';
function WrapInProvider(store: any, Component: any, props: any) {
return (
@ -56,9 +57,8 @@ export function reactContainer(
};
document.body.classList.add('is-react');
ReactDOM.render(WrapInProvider(store, provideTheme(component), props), elem[0]);
initialiseVariableSuggestions($injector.get('templateSrv'));
scope.$on('$destroy', () => {
document.body.classList.remove('is-react');
ReactDOM.unmountComponentAtNode(elem[0]);