mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Refactor: consistant plugin/meta usage (#16834)
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
import { ComponentClass } from 'react';
|
import { ComponentClass } from 'react';
|
||||||
import { TimeRange } from './time';
|
import { TimeRange } from './time';
|
||||||
import { PluginMeta } from './plugin';
|
import { PluginMeta, GrafanaPlugin } from './plugin';
|
||||||
import { TableData, TimeSeries, SeriesData, LoadingState } from './data';
|
import { TableData, TimeSeries, SeriesData, LoadingState } from './data';
|
||||||
import { PanelData } from './panel';
|
import { PanelData } from './panel';
|
||||||
|
|
||||||
@@ -8,11 +8,14 @@ export interface DataSourcePluginOptionsEditorProps<TOptions> {
|
|||||||
options: TOptions;
|
options: TOptions;
|
||||||
onOptionsChange: (options: TOptions) => void;
|
onOptionsChange: (options: TOptions) => void;
|
||||||
}
|
}
|
||||||
export class DataSourcePlugin<TOptions = {}, TQuery extends DataQuery = DataQuery> {
|
export class DataSourcePlugin<TOptions = {}, TQuery extends DataQuery = DataQuery> extends GrafanaPlugin<
|
||||||
|
DataSourcePluginMeta
|
||||||
|
> {
|
||||||
DataSourceClass: DataSourceConstructor<TQuery>;
|
DataSourceClass: DataSourceConstructor<TQuery>;
|
||||||
components: DataSourcePluginComponents<TOptions, TQuery>;
|
components: DataSourcePluginComponents<TOptions, TQuery>;
|
||||||
|
|
||||||
constructor(DataSourceClass: DataSourceConstructor<TQuery>) {
|
constructor(DataSourceClass: DataSourceConstructor<TQuery>) {
|
||||||
|
super();
|
||||||
this.DataSourceClass = DataSourceClass;
|
this.DataSourceClass = DataSourceClass;
|
||||||
this.components = {};
|
this.components = {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,16 +2,13 @@ import { ComponentClass, ComponentType } from 'react';
|
|||||||
import { LoadingState, SeriesData } from './data';
|
import { LoadingState, SeriesData } from './data';
|
||||||
import { TimeRange } from './time';
|
import { TimeRange } from './time';
|
||||||
import { ScopedVars, DataQueryRequest, DataQueryError, LegacyResponseData } from './datasource';
|
import { ScopedVars, DataQueryRequest, DataQueryError, LegacyResponseData } from './datasource';
|
||||||
import { PluginMeta } from './plugin';
|
import { PluginMeta, GrafanaPlugin } from './plugin';
|
||||||
|
|
||||||
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
|
export type InterpolateFunction = (value: string, scopedVars?: ScopedVars, format?: string | Function) => string;
|
||||||
|
|
||||||
export interface PanelPluginMeta extends PluginMeta {
|
export interface PanelPluginMeta extends PluginMeta {
|
||||||
hideFromList?: boolean;
|
hideFromList?: boolean;
|
||||||
sort: number;
|
sort: number;
|
||||||
angularPlugin: AngularPanelPlugin | null;
|
|
||||||
panelPlugin: PanelPlugin | null;
|
|
||||||
hasBeenImported?: boolean;
|
|
||||||
|
|
||||||
// if length>0 the query tab will show up
|
// if length>0 the query tab will show up
|
||||||
// Before 6.2 this could be table and/or series, but 6.2+ supports both transparently
|
// Before 6.2 this could be table and/or series, but 6.2+ supports both transparently
|
||||||
@@ -72,14 +69,20 @@ export type PanelTypeChangedHandler<TOptions = any> = (
|
|||||||
prevOptions: any
|
prevOptions: any
|
||||||
) => Partial<TOptions>;
|
) => Partial<TOptions>;
|
||||||
|
|
||||||
export class PanelPlugin<TOptions = any> {
|
export class PanelPlugin<TOptions = any> extends GrafanaPlugin<PanelPluginMeta> {
|
||||||
panel: ComponentType<PanelProps<TOptions>>;
|
panel: ComponentType<PanelProps<TOptions>>;
|
||||||
editor?: ComponentClass<PanelEditorProps<TOptions>>;
|
editor?: ComponentClass<PanelEditorProps<TOptions>>;
|
||||||
defaults?: TOptions;
|
defaults?: TOptions;
|
||||||
onPanelMigration?: PanelMigrationHandler<TOptions>;
|
onPanelMigration?: PanelMigrationHandler<TOptions>;
|
||||||
onPanelTypeChanged?: PanelTypeChangedHandler<TOptions>;
|
onPanelTypeChanged?: PanelTypeChangedHandler<TOptions>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy angular ctrl. If this exists it will be used instead of the panel
|
||||||
|
*/
|
||||||
|
angularPanelCtrl?: any;
|
||||||
|
|
||||||
constructor(panel: ComponentType<PanelProps<TOptions>>) {
|
constructor(panel: ComponentType<PanelProps<TOptions>>) {
|
||||||
|
super();
|
||||||
this.panel = panel;
|
this.panel = panel;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,16 +117,6 @@ export class PanelPlugin<TOptions = any> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AngularPanelPlugin {
|
|
||||||
components: {
|
|
||||||
PanelCtrl: any;
|
|
||||||
};
|
|
||||||
|
|
||||||
constructor(PanelCtrl: any) {
|
|
||||||
this.components = { PanelCtrl: PanelCtrl };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface PanelSize {
|
export interface PanelSize {
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
|
|||||||
@@ -69,16 +69,20 @@ export interface PluginMetaInfo {
|
|||||||
version: string;
|
version: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class AppPlugin {
|
export class GrafanaPlugin<T extends PluginMeta> {
|
||||||
meta: PluginMeta;
|
// Meta is filled in by the plugin loading system
|
||||||
|
meta?: T;
|
||||||
|
|
||||||
|
// Soon this will also include common config options
|
||||||
|
}
|
||||||
|
|
||||||
|
export class AppPlugin extends GrafanaPlugin<PluginMeta> {
|
||||||
angular?: {
|
angular?: {
|
||||||
ConfigCtrl?: any;
|
ConfigCtrl?: any;
|
||||||
pages: { [component: string]: any };
|
pages: { [component: string]: any };
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(meta: PluginMeta, pluginExports: any) {
|
setComponentsFromLegacyExports(pluginExports: any) {
|
||||||
this.meta = meta;
|
|
||||||
const legacy = {
|
const legacy = {
|
||||||
ConfigCtrl: undefined,
|
ConfigCtrl: undefined,
|
||||||
pages: {} as any,
|
pages: {} as any,
|
||||||
@@ -89,7 +93,8 @@ export class AppPlugin {
|
|||||||
this.angular = legacy;
|
this.angular = legacy;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meta.includes) {
|
const { meta } = this;
|
||||||
|
if (meta && meta.includes) {
|
||||||
for (const include of meta.includes) {
|
for (const include of meta.includes) {
|
||||||
const { type, component } = include;
|
const { type, component } = include;
|
||||||
if (type === PluginIncludeType.page && component) {
|
if (type === PluginIncludeType.page && component) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// Libraries
|
// Libraries
|
||||||
import React, { PureComponent } from 'react';
|
import React, { PureComponent } from 'react';
|
||||||
import config from 'app/core/config';
|
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
|
|
||||||
// Utils & Services
|
// Utils & Services
|
||||||
@@ -9,7 +8,6 @@ import { importPanelPlugin } from 'app/features/plugins/plugin_loader';
|
|||||||
|
|
||||||
// Components
|
// Components
|
||||||
import { AddPanelWidget } from '../components/AddPanelWidget';
|
import { AddPanelWidget } from '../components/AddPanelWidget';
|
||||||
import { getPanelPluginNotFound } from './PanelPluginNotFound';
|
|
||||||
import { DashboardRow } from '../components/DashboardRow';
|
import { DashboardRow } from '../components/DashboardRow';
|
||||||
import { PanelChrome } from './PanelChrome';
|
import { PanelChrome } from './PanelChrome';
|
||||||
import { PanelEditor } from '../panel_editor/PanelEditor';
|
import { PanelEditor } from '../panel_editor/PanelEditor';
|
||||||
@@ -17,7 +15,7 @@ import { PanelResizer } from './PanelResizer';
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { PanelModel, DashboardModel } from '../state';
|
import { PanelModel, DashboardModel } from '../state';
|
||||||
import { PanelPluginMeta, AngularPanelPlugin, PanelPlugin } from '@grafana/ui/src/types/panel';
|
import { PanelPluginMeta, PanelPlugin } from '@grafana/ui/src/types/panel';
|
||||||
import { AutoSizer } from 'react-virtualized';
|
import { AutoSizer } from 'react-virtualized';
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
@@ -28,7 +26,7 @@ export interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface State {
|
export interface State {
|
||||||
plugin: PanelPluginMeta;
|
plugin: PanelPlugin;
|
||||||
angularPanel: AngularComponent;
|
angularPanel: AngularComponent;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -72,15 +70,12 @@ export class DashboardPanel extends PureComponent<Props, State> {
|
|||||||
const { panel } = this.props;
|
const { panel } = this.props;
|
||||||
|
|
||||||
// handle plugin loading & changing of plugin type
|
// handle plugin loading & changing of plugin type
|
||||||
if (!this.state.plugin || this.state.plugin.id !== pluginId) {
|
if (!this.state.plugin || this.state.plugin.meta.id !== pluginId) {
|
||||||
let plugin = config.panels[pluginId] || getPanelPluginNotFound(pluginId);
|
const plugin = await importPanelPlugin(pluginId);
|
||||||
|
|
||||||
// unmount angular panel
|
// unmount angular panel
|
||||||
this.cleanUpAngularPanel();
|
this.cleanUpAngularPanel();
|
||||||
|
|
||||||
// load the actual plugin code
|
|
||||||
plugin = await this.importPanelPluginModule(plugin);
|
|
||||||
|
|
||||||
if (panel.type !== pluginId) {
|
if (panel.type !== pluginId) {
|
||||||
panel.changePlugin(plugin);
|
panel.changePlugin(plugin);
|
||||||
} else {
|
} else {
|
||||||
@@ -91,27 +86,6 @@ export class DashboardPanel extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async importPanelPluginModule(plugin: PanelPluginMeta): Promise<PanelPluginMeta> {
|
|
||||||
if (plugin.hasBeenImported) {
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const importedPlugin = await importPanelPlugin(plugin.module);
|
|
||||||
if (importedPlugin instanceof AngularPanelPlugin) {
|
|
||||||
plugin.angularPlugin = importedPlugin as AngularPanelPlugin;
|
|
||||||
} else if (importedPlugin instanceof PanelPlugin) {
|
|
||||||
plugin.panelPlugin = importedPlugin as PanelPlugin;
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
plugin = getPanelPluginNotFound(plugin.id);
|
|
||||||
console.log('Failed to import plugin module', e);
|
|
||||||
}
|
|
||||||
|
|
||||||
plugin.hasBeenImported = true;
|
|
||||||
return plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.loadPlugin(this.props.panel.type);
|
this.loadPlugin(this.props.panel.type);
|
||||||
}
|
}
|
||||||
@@ -186,7 +160,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// if we have not loaded plugin exports yet, wait
|
// if we have not loaded plugin exports yet, wait
|
||||||
if (!plugin || !plugin.hasBeenImported) {
|
if (!plugin) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -209,8 +183,7 @@ export class DashboardPanel extends PureComponent<Props, State> {
|
|||||||
onMouseLeave={this.onMouseLeave}
|
onMouseLeave={this.onMouseLeave}
|
||||||
style={styles}
|
style={styles}
|
||||||
>
|
>
|
||||||
{plugin.panelPlugin && this.renderReactPanel()}
|
{plugin.angularPanelCtrl ? this.renderAngularPanel() : this.renderReactPanel()}
|
||||||
{plugin.angularPlugin && this.renderAngularPanel()}
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ import config from 'app/core/config';
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { DashboardModel, PanelModel } from '../state';
|
import { DashboardModel, PanelModel } from '../state';
|
||||||
import { PanelPluginMeta, LoadingState, PanelData } from '@grafana/ui';
|
import { LoadingState, PanelData, PanelPlugin } from '@grafana/ui';
|
||||||
import { ScopedVars } from '@grafana/ui';
|
import { ScopedVars } from '@grafana/ui';
|
||||||
|
|
||||||
import templateSrv from 'app/features/templating/template_srv';
|
import templateSrv from 'app/features/templating/template_srv';
|
||||||
@@ -29,7 +29,7 @@ const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
|
|||||||
export interface Props {
|
export interface Props {
|
||||||
panel: PanelModel;
|
panel: PanelModel;
|
||||||
dashboard: DashboardModel;
|
dashboard: DashboardModel;
|
||||||
plugin: PanelPluginMeta;
|
plugin: PanelPlugin;
|
||||||
isFullscreen: boolean;
|
isFullscreen: boolean;
|
||||||
width: number;
|
width: number;
|
||||||
height: number;
|
height: number;
|
||||||
@@ -209,13 +209,13 @@ export class PanelChrome extends PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get wantsQueryExecution() {
|
get wantsQueryExecution() {
|
||||||
return this.props.plugin.dataFormats.length > 0 && !this.hasPanelSnapshot;
|
return this.props.plugin.meta.dataFormats.length > 0 && !this.hasPanelSnapshot;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPanel(width: number, height: number): JSX.Element {
|
renderPanel(width: number, height: number): JSX.Element {
|
||||||
const { panel, plugin } = this.props;
|
const { panel, plugin } = this.props;
|
||||||
const { renderCounter, data, isFirstLoad } = this.state;
|
const { renderCounter, data, isFirstLoad } = this.state;
|
||||||
const PanelComponent = plugin.panelPlugin.panel;
|
const PanelComponent = plugin.panel;
|
||||||
|
|
||||||
// This is only done to increase a counter that is used by backend
|
// This is only done to increase a counter that is used by backend
|
||||||
// image rendering (phantomjs/headless chrome) to know when to capture image
|
// image rendering (phantomjs/headless chrome) to know when to capture image
|
||||||
@@ -236,7 +236,7 @@ export class PanelChrome extends PureComponent<Props, State> {
|
|||||||
<PanelComponent
|
<PanelComponent
|
||||||
data={data}
|
data={data}
|
||||||
timeRange={data.request ? data.request.range : this.timeSrv.timeRange()}
|
timeRange={data.request ? data.request.range : this.timeSrv.timeRange()}
|
||||||
options={panel.getOptions(plugin.panelPlugin.defaults)}
|
options={panel.getOptions(plugin.defaults)}
|
||||||
width={width - 2 * config.theme.panelPadding.horizontal}
|
width={width - 2 * config.theme.panelPadding.horizontal}
|
||||||
height={height - PANEL_HEADER_HEIGHT - config.theme.panelPadding.vertical}
|
height={height - PANEL_HEADER_HEIGHT - config.theme.panelPadding.vertical}
|
||||||
renderCounter={renderCounter}
|
renderCounter={renderCounter}
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ import { AlertBox } from 'app/core/components/AlertBox/AlertBox';
|
|||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { AppNotificationSeverity } from 'app/types';
|
import { AppNotificationSeverity } from 'app/types';
|
||||||
import { PanelPluginMeta, PanelProps, PanelPlugin, PluginType } from '@grafana/ui';
|
import { PanelProps, PanelPlugin, PluginType } from '@grafana/ui';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
pluginId: string;
|
pluginId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
class PanelPluginNotFound extends PureComponent<Props> {
|
class PanelPluginNotFound extends PureComponent<Props> {
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -34,14 +34,15 @@ class PanelPluginNotFound extends PureComponent<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getPanelPluginNotFound(id: string): PanelPluginMeta {
|
export function getPanelPluginNotFound(id: string): PanelPlugin {
|
||||||
const NotFound = class NotFound extends PureComponent<PanelProps> {
|
const NotFound = class NotFound extends PureComponent<PanelProps> {
|
||||||
render() {
|
render() {
|
||||||
return <PanelPluginNotFound pluginId={id} />;
|
return <PanelPluginNotFound pluginId={id} />;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
const plugin = new PanelPlugin(NotFound);
|
||||||
|
plugin.meta = {
|
||||||
id: id,
|
id: id,
|
||||||
name: id,
|
name: id,
|
||||||
sort: 100,
|
sort: 100,
|
||||||
@@ -63,7 +64,6 @@ export function getPanelPluginNotFound(id: string): PanelPluginMeta {
|
|||||||
updated: '',
|
updated: '',
|
||||||
version: '',
|
version: '',
|
||||||
},
|
},
|
||||||
panelPlugin: new PanelPlugin(NotFound),
|
|
||||||
angularPlugin: null,
|
|
||||||
};
|
};
|
||||||
|
return plugin;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,12 @@ import { AngularComponent } from 'app/core/services/AngularLoader';
|
|||||||
|
|
||||||
import { PanelModel } from '../state/PanelModel';
|
import { PanelModel } from '../state/PanelModel';
|
||||||
import { DashboardModel } from '../state/DashboardModel';
|
import { DashboardModel } from '../state/DashboardModel';
|
||||||
import { PanelPluginMeta, Tooltip } from '@grafana/ui';
|
import { Tooltip, PanelPlugin, PanelPluginMeta } from '@grafana/ui';
|
||||||
|
|
||||||
interface PanelEditorProps {
|
interface PanelEditorProps {
|
||||||
panel: PanelModel;
|
panel: PanelModel;
|
||||||
dashboard: DashboardModel;
|
dashboard: DashboardModel;
|
||||||
plugin: PanelPluginMeta;
|
plugin: PanelPlugin;
|
||||||
angularPanel?: AngularComponent;
|
angularPanel?: AngularComponent;
|
||||||
onTypeChanged: (newType: PanelPluginMeta) => void;
|
onTypeChanged: (newType: PanelPluginMeta) => void;
|
||||||
}
|
}
|
||||||
@@ -55,7 +55,7 @@ const getPanelEditorTab = (tabId: PanelEditorTabIds): PanelEditorTab => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export class PanelEditor extends PureComponent<PanelEditorProps> {
|
export class PanelEditor extends PureComponent<PanelEditorProps> {
|
||||||
constructor(props) {
|
constructor(props: PanelEditorProps) {
|
||||||
super(props);
|
super(props);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
|
|||||||
];
|
];
|
||||||
|
|
||||||
// handle panels that do not have queries tab
|
// handle panels that do not have queries tab
|
||||||
if (plugin.dataFormats.length === 0) {
|
if (plugin.meta.dataFormats.length === 0) {
|
||||||
// remove queries tab
|
// remove queries tab
|
||||||
tabs.shift();
|
tabs.shift();
|
||||||
// switch tab
|
// switch tab
|
||||||
@@ -114,7 +114,7 @@ export class PanelEditor extends PureComponent<PanelEditorProps> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.alertingEnabled && plugin.id === 'graph') {
|
if (config.alertingEnabled && plugin.meta.id === 'graph') {
|
||||||
tabs.push(getPanelEditorTab(PanelEditorTabIds.Alert));
|
tabs.push(getPanelEditorTab(PanelEditorTabIds.Alert));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,12 +18,12 @@ import { PanelModel } from '../state';
|
|||||||
import { DashboardModel } from '../state';
|
import { DashboardModel } from '../state';
|
||||||
import { VizPickerSearch } from './VizPickerSearch';
|
import { VizPickerSearch } from './VizPickerSearch';
|
||||||
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
|
import PluginStateinfo from 'app/features/plugins/PluginStateInfo';
|
||||||
import { PanelPluginMeta } from '@grafana/ui';
|
import { PanelPlugin, PanelPluginMeta } from '@grafana/ui';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
panel: PanelModel;
|
panel: PanelModel;
|
||||||
dashboard: DashboardModel;
|
dashboard: DashboardModel;
|
||||||
plugin: PanelPluginMeta;
|
plugin: PanelPlugin;
|
||||||
angularPanel?: AngularComponent;
|
angularPanel?: AngularComponent;
|
||||||
onTypeChanged: (newType: PanelPluginMeta) => void;
|
onTypeChanged: (newType: PanelPluginMeta) => void;
|
||||||
updateLocation: typeof updateLocation;
|
updateLocation: typeof updateLocation;
|
||||||
@@ -41,7 +41,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
element: HTMLElement;
|
element: HTMLElement;
|
||||||
angularOptions: AngularComponent;
|
angularOptions: AngularComponent;
|
||||||
|
|
||||||
constructor(props) {
|
constructor(props: Props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
|
||||||
this.state = {
|
this.state = {
|
||||||
@@ -54,7 +54,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
|
|
||||||
getReactPanelOptions = () => {
|
getReactPanelOptions = () => {
|
||||||
const { panel, plugin } = this.props;
|
const { panel, plugin } = this.props;
|
||||||
return panel.getOptions(plugin.panelPlugin.defaults);
|
return panel.getOptions(plugin.defaults);
|
||||||
};
|
};
|
||||||
|
|
||||||
renderPanelOptions() {
|
renderPanelOptions() {
|
||||||
@@ -64,12 +64,8 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
return <div ref={element => (this.element = element)} />;
|
return <div ref={element => (this.element = element)} />;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (plugin.panelPlugin) {
|
if (plugin.editor) {
|
||||||
const PanelEditor = plugin.panelPlugin.editor;
|
return <plugin.editor options={this.getReactPanelOptions()} onOptionsChange={this.onPanelOptionsChanged} />;
|
||||||
|
|
||||||
if (PanelEditor) {
|
|
||||||
return <PanelEditor options={this.getReactPanelOptions()} onOptionsChange={this.onPanelOptionsChanged} />;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return <p>Visualization has no options</p>;
|
return <p>Visualization has no options</p>;
|
||||||
@@ -176,11 +172,12 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
renderToolbar = (): JSX.Element => {
|
renderToolbar = (): JSX.Element => {
|
||||||
const { plugin } = this.props;
|
const { plugin } = this.props;
|
||||||
const { isVizPickerOpen, searchQuery } = this.state;
|
const { isVizPickerOpen, searchQuery } = this.state;
|
||||||
|
const { meta } = plugin;
|
||||||
|
|
||||||
if (isVizPickerOpen) {
|
if (isVizPickerOpen) {
|
||||||
return (
|
return (
|
||||||
<VizPickerSearch
|
<VizPickerSearch
|
||||||
plugin={plugin}
|
plugin={meta}
|
||||||
searchQuery={searchQuery}
|
searchQuery={searchQuery}
|
||||||
onChange={this.onSearchQueryChange}
|
onChange={this.onSearchQueryChange}
|
||||||
onClose={this.onCloseVizPicker}
|
onClose={this.onCloseVizPicker}
|
||||||
@@ -189,8 +186,8 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div className="toolbar__main" onClick={this.onOpenVizPicker}>
|
<div className="toolbar__main" onClick={this.onOpenVizPicker}>
|
||||||
<img className="toolbar__main-image" src={plugin.info.logos.small} />
|
<img className="toolbar__main-image" src={meta.info.logos.small} />
|
||||||
<div className="toolbar__main-name">{plugin.name}</div>
|
<div className="toolbar__main-name">{meta.name}</div>
|
||||||
<i className="fa fa-caret-down" />
|
<i className="fa fa-caret-down" />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
@@ -198,14 +195,14 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
onTypeChanged = (plugin: PanelPluginMeta) => {
|
onTypeChanged = (plugin: PanelPluginMeta) => {
|
||||||
if (plugin.id === this.props.plugin.id) {
|
if (plugin.id === this.props.plugin.meta.id) {
|
||||||
this.setState({ isVizPickerOpen: false });
|
this.setState({ isVizPickerOpen: false });
|
||||||
} else {
|
} else {
|
||||||
this.props.onTypeChanged(plugin);
|
this.props.onTypeChanged(plugin);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
renderHelp = () => <PluginHelp plugin={this.props.plugin} type="help" />;
|
renderHelp = () => <PluginHelp plugin={this.props.plugin.meta} type="help" />;
|
||||||
|
|
||||||
setScrollTop = (event: React.MouseEvent<HTMLElement>) => {
|
setScrollTop = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
const target = event.target as HTMLElement;
|
const target = event.target as HTMLElement;
|
||||||
@@ -215,6 +212,7 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
render() {
|
render() {
|
||||||
const { plugin } = this.props;
|
const { plugin } = this.props;
|
||||||
const { isVizPickerOpen, searchQuery, scrollTop } = this.state;
|
const { isVizPickerOpen, searchQuery, scrollTop } = this.state;
|
||||||
|
const { meta } = plugin;
|
||||||
|
|
||||||
const pluginHelp: EditorToolbarView = {
|
const pluginHelp: EditorToolbarView = {
|
||||||
heading: 'Help',
|
heading: 'Help',
|
||||||
@@ -233,13 +231,13 @@ export class VisualizationTab extends PureComponent<Props, State> {
|
|||||||
<>
|
<>
|
||||||
<FadeIn in={isVizPickerOpen} duration={200} unmountOnExit={true} onExited={this.clearQuery}>
|
<FadeIn in={isVizPickerOpen} duration={200} unmountOnExit={true} onExited={this.clearQuery}>
|
||||||
<VizTypePicker
|
<VizTypePicker
|
||||||
current={plugin}
|
current={meta}
|
||||||
onTypeChanged={this.onTypeChanged}
|
onTypeChanged={this.onTypeChanged}
|
||||||
searchQuery={searchQuery}
|
searchQuery={searchQuery}
|
||||||
onClose={this.onCloseVizPicker}
|
onClose={this.onCloseVizPicker}
|
||||||
/>
|
/>
|
||||||
</FadeIn>
|
</FadeIn>
|
||||||
<PluginStateinfo state={plugin.state} />
|
<PluginStateinfo state={meta.state} />
|
||||||
{this.renderPanelOptions()}
|
{this.renderPanelOptions()}
|
||||||
</>
|
</>
|
||||||
</EditorTabBody>
|
</EditorTabBody>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { PanelModel } from './PanelModel';
|
import { PanelModel } from './PanelModel';
|
||||||
import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks';
|
import { getPanelPlugin } from '../../plugins/__mocks__/pluginMocks';
|
||||||
import { PanelPlugin, AngularPanelPlugin } from '@grafana/ui/src/types/panel';
|
|
||||||
|
|
||||||
class TablePanelCtrl {}
|
class TablePanelCtrl {}
|
||||||
|
|
||||||
@@ -31,10 +30,13 @@ describe('PanelModel', () => {
|
|||||||
};
|
};
|
||||||
model = new PanelModel(modelJson);
|
model = new PanelModel(modelJson);
|
||||||
model.pluginLoaded(
|
model.pluginLoaded(
|
||||||
getPanelPlugin({
|
getPanelPlugin(
|
||||||
|
{
|
||||||
id: 'table',
|
id: 'table',
|
||||||
angularPlugin: new AngularPanelPlugin(TablePanelCtrl),
|
},
|
||||||
})
|
null, // react
|
||||||
|
TablePanelCtrl // angular
|
||||||
|
)
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -123,15 +125,10 @@ describe('PanelModel', () => {
|
|||||||
|
|
||||||
describe('when changing to react panel', () => {
|
describe('when changing to react panel', () => {
|
||||||
const onPanelTypeChanged = jest.fn();
|
const onPanelTypeChanged = jest.fn();
|
||||||
const reactPlugin = new PanelPlugin({} as any).setPanelChangeHandler(onPanelTypeChanged as any);
|
const reactPlugin = getPanelPlugin({ id: 'react' }).setPanelChangeHandler(onPanelTypeChanged as any);
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
model.changePlugin(
|
model.changePlugin(reactPlugin);
|
||||||
getPanelPlugin({
|
|
||||||
id: 'react',
|
|
||||||
panelPlugin: reactPlugin,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call react onPanelTypeChanged', () => {
|
it('should call react onPanelTypeChanged', () => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { Emitter } from 'app/core/utils/emitter';
|
|||||||
import { getNextRefIdChar } from 'app/core/utils/query';
|
import { getNextRefIdChar } from 'app/core/utils/query';
|
||||||
|
|
||||||
// Types
|
// Types
|
||||||
import { PanelPluginMeta, DataQuery, Threshold, ScopedVars, DataQueryResponseData } from '@grafana/ui';
|
import { DataQuery, Threshold, ScopedVars, DataQueryResponseData, PanelPlugin } from '@grafana/ui';
|
||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
|
|
||||||
import { PanelQueryRunner } from './PanelQueryRunner';
|
import { PanelQueryRunner } from './PanelQueryRunner';
|
||||||
@@ -116,7 +116,7 @@ export class PanelModel {
|
|||||||
cacheTimeout?: any;
|
cacheTimeout?: any;
|
||||||
cachedPluginOptions?: any;
|
cachedPluginOptions?: any;
|
||||||
legend?: { show: boolean };
|
legend?: { show: boolean };
|
||||||
plugin?: PanelPluginMeta;
|
plugin?: PanelPlugin;
|
||||||
private queryRunner?: PanelQueryRunner;
|
private queryRunner?: PanelQueryRunner;
|
||||||
|
|
||||||
constructor(model: any) {
|
constructor(model: any) {
|
||||||
@@ -248,29 +248,25 @@ export class PanelModel {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private getPluginVersion(plugin: PanelPluginMeta): string {
|
pluginLoaded(plugin: PanelPlugin) {
|
||||||
return this.plugin && this.plugin.info.version ? this.plugin.info.version : config.buildInfo.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
pluginLoaded(plugin: PanelPluginMeta) {
|
|
||||||
this.plugin = plugin;
|
this.plugin = plugin;
|
||||||
|
|
||||||
if (plugin.panelPlugin && plugin.panelPlugin.onPanelMigration) {
|
if (plugin.panel && plugin.onPanelMigration) {
|
||||||
const version = this.getPluginVersion(plugin);
|
const version = getPluginVersion(plugin);
|
||||||
if (version !== this.pluginVersion) {
|
if (version !== this.pluginVersion) {
|
||||||
this.options = plugin.panelPlugin.onPanelMigration(this);
|
this.options = plugin.onPanelMigration(this);
|
||||||
this.pluginVersion = version;
|
this.pluginVersion = version;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
changePlugin(newPlugin: PanelPluginMeta) {
|
changePlugin(newPlugin: PanelPlugin) {
|
||||||
const pluginId = newPlugin.id;
|
const pluginId = newPlugin.meta.id;
|
||||||
const oldOptions: any = this.getOptionsToRemember();
|
const oldOptions: any = this.getOptionsToRemember();
|
||||||
const oldPluginId = this.type;
|
const oldPluginId = this.type;
|
||||||
|
|
||||||
// for angular panels we must remove all events and let angular panels do some cleanup
|
// for angular panels we must remove all events and let angular panels do some cleanup
|
||||||
if (this.plugin.angularPlugin) {
|
if (this.plugin.angularPanelCtrl) {
|
||||||
this.destroy();
|
this.destroy();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,18 +287,14 @@ export class PanelModel {
|
|||||||
this.plugin = newPlugin;
|
this.plugin = newPlugin;
|
||||||
|
|
||||||
// Let panel plugins inspect options from previous panel and keep any that it can use
|
// Let panel plugins inspect options from previous panel and keep any that it can use
|
||||||
const reactPanel = newPlugin.panelPlugin;
|
if (newPlugin.onPanelTypeChanged) {
|
||||||
|
|
||||||
if (reactPanel) {
|
|
||||||
if (reactPanel.onPanelTypeChanged) {
|
|
||||||
this.options = this.options || {};
|
this.options = this.options || {};
|
||||||
const old = oldOptions && oldOptions.options ? oldOptions.options : {};
|
const old = oldOptions && oldOptions.options ? oldOptions.options : {};
|
||||||
Object.assign(this.options, reactPanel.onPanelTypeChanged(this.options, oldPluginId, old));
|
Object.assign(this.options, newPlugin.onPanelTypeChanged(this.options, oldPluginId, old));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (reactPanel.onPanelMigration) {
|
if (newPlugin.onPanelMigration) {
|
||||||
this.pluginVersion = this.getPluginVersion(newPlugin);
|
this.pluginVersion = getPluginVersion(newPlugin);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -341,3 +333,7 @@ export class PanelModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPluginVersion(plugin: PanelPlugin): string {
|
||||||
|
return plugin && plugin.meta.info.version ? plugin.meta.info.version : config.buildInfo.version;
|
||||||
|
}
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ export class DataSourceSettingsPage extends PureComponent<Props, State> {
|
|||||||
let importedPlugin: DataSourcePlugin;
|
let importedPlugin: DataSourcePlugin;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
importedPlugin = await importDataSourcePlugin(dataSourceMeta.module);
|
importedPlugin = await importDataSourcePlugin(dataSourceMeta);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log('Failed to import plugin module', e);
|
console.log('Failed to import plugin module', e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { PanelPluginMeta, PluginMeta, PluginType, PanelDataFormat } from '@grafana/ui';
|
import { PanelPluginMeta, PluginMeta, PluginType, PanelDataFormat, PanelPlugin, PanelProps } from '@grafana/ui';
|
||||||
|
import { ComponentType } from 'enzyme';
|
||||||
|
|
||||||
export const getMockPlugins = (amount: number): PluginMeta[] => {
|
export const getMockPlugins = (amount: number): PluginMeta[] => {
|
||||||
const plugins = [];
|
const plugins = [];
|
||||||
@@ -33,8 +34,14 @@ export const getMockPlugins = (amount: number): PluginMeta[] => {
|
|||||||
return plugins;
|
return plugins;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getPanelPlugin = (options: Partial<PanelPluginMeta>): PanelPluginMeta => {
|
export const getPanelPlugin = (
|
||||||
return {
|
options: Partial<PanelPluginMeta>,
|
||||||
|
reactPanel?: ComponentType<PanelProps>,
|
||||||
|
angularPanel?: any
|
||||||
|
): PanelPlugin => {
|
||||||
|
const plugin = new PanelPlugin(reactPanel);
|
||||||
|
plugin.angularPanelCtrl = angularPanel;
|
||||||
|
plugin.meta = {
|
||||||
id: options.id,
|
id: options.id,
|
||||||
type: PluginType.panel,
|
type: PluginType.panel,
|
||||||
name: options.id,
|
name: options.id,
|
||||||
@@ -57,9 +64,8 @@ export const getPanelPlugin = (options: Partial<PanelPluginMeta>): PanelPluginMe
|
|||||||
hideFromList: options.hideFromList === true,
|
hideFromList: options.hideFromList === true,
|
||||||
module: '',
|
module: '',
|
||||||
baseUrl: '',
|
baseUrl: '',
|
||||||
panelPlugin: options.panelPlugin,
|
|
||||||
angularPlugin: options.angularPlugin,
|
|
||||||
};
|
};
|
||||||
|
return plugin;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const getMockPlugin = () => {
|
export const getMockPlugin = () => {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export class DatasourceSrv {
|
|||||||
|
|
||||||
const deferred = this.$q.defer();
|
const deferred = this.$q.defer();
|
||||||
|
|
||||||
importDataSourcePlugin(dsConfig.meta.module)
|
importDataSourcePlugin(dsConfig.meta)
|
||||||
.then(dsPlugin => {
|
.then(dsPlugin => {
|
||||||
// check if its in cache now
|
// check if its in cache now
|
||||||
if (this.datasources[name]) {
|
if (this.datasources[name]) {
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import _ from 'lodash';
|
|||||||
import config from 'app/core/config';
|
import config from 'app/core/config';
|
||||||
import coreModule from 'app/core/core_module';
|
import coreModule from 'app/core/core_module';
|
||||||
|
|
||||||
import { AngularPanelPlugin, DataSourceApi } from '@grafana/ui/src/types';
|
import { DataSourceApi } from '@grafana/ui/src/types';
|
||||||
import { importPanelPlugin, importDataSourcePlugin, importAppPlugin } from './plugin_loader';
|
import { importPanelPlugin, importDataSourcePlugin, importAppPlugin } from './plugin_loader';
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
@@ -22,7 +22,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function relativeTemplateUrlToAbs(templateUrl, baseUrl) {
|
function relativeTemplateUrlToAbs(templateUrl: string, baseUrl: string) {
|
||||||
if (!templateUrl) {
|
if (!templateUrl) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
@@ -69,9 +69,8 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
|||||||
};
|
};
|
||||||
|
|
||||||
const panelInfo = config.panels[scope.panel.type];
|
const panelInfo = config.panels[scope.panel.type];
|
||||||
return importPanelPlugin(panelInfo.module).then(panelPlugin => {
|
return importPanelPlugin(panelInfo.id).then(panelPlugin => {
|
||||||
const angularPanelPlugin = panelPlugin as AngularPanelPlugin;
|
const PanelCtrl = panelPlugin.angularPanelCtrl;
|
||||||
const PanelCtrl = angularPanelPlugin.components.PanelCtrl;
|
|
||||||
componentInfo.Component = PanelCtrl;
|
componentInfo.Component = PanelCtrl;
|
||||||
|
|
||||||
if (!PanelCtrl || PanelCtrl.registered) {
|
if (!PanelCtrl || PanelCtrl.registered) {
|
||||||
@@ -118,7 +117,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
|||||||
}
|
}
|
||||||
// Annotations
|
// Annotations
|
||||||
case 'annotations-query-ctrl': {
|
case 'annotations-query-ctrl': {
|
||||||
return importDataSourcePlugin(scope.ctrl.currentDatasource.meta.module).then(dsPlugin => {
|
return importDataSourcePlugin(scope.ctrl.currentDatasource.meta).then(dsPlugin => {
|
||||||
return {
|
return {
|
||||||
baseUrl: scope.ctrl.currentDatasource.meta.baseUrl,
|
baseUrl: scope.ctrl.currentDatasource.meta.baseUrl,
|
||||||
name: 'annotations-query-ctrl-' + scope.ctrl.currentDatasource.meta.id,
|
name: 'annotations-query-ctrl-' + scope.ctrl.currentDatasource.meta.id,
|
||||||
@@ -134,7 +133,7 @@ function pluginDirectiveLoader($compile, datasourceSrv, $rootScope, $q, $http, $
|
|||||||
// Datasource ConfigCtrl
|
// Datasource ConfigCtrl
|
||||||
case 'datasource-config-ctrl': {
|
case 'datasource-config-ctrl': {
|
||||||
const dsMeta = scope.ctrl.datasourceMeta;
|
const dsMeta = scope.ctrl.datasourceMeta;
|
||||||
return importDataSourcePlugin(dsMeta.module).then(dsPlugin => {
|
return importDataSourcePlugin(dsMeta).then(dsPlugin => {
|
||||||
scope.$watch(
|
scope.$watch(
|
||||||
'ctrl.current',
|
'ctrl.current',
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import config from 'app/core/config';
|
|||||||
import TimeSeries from 'app/core/time_series2';
|
import TimeSeries from 'app/core/time_series2';
|
||||||
import TableModel from 'app/core/table_model';
|
import TableModel from 'app/core/table_model';
|
||||||
import { coreModule, appEvents, contextSrv } from 'app/core/core';
|
import { coreModule, appEvents, contextSrv } from 'app/core/core';
|
||||||
import { DataSourcePlugin, AppPlugin, PanelPlugin, AngularPanelPlugin, PluginMeta } from '@grafana/ui/src/types';
|
import { DataSourcePlugin, AppPlugin, PanelPlugin, PluginMeta, DataSourcePluginMeta } from '@grafana/ui/src/types';
|
||||||
import * as datemath from 'app/core/utils/datemath';
|
import * as datemath from 'app/core/utils/datemath';
|
||||||
import * as fileExport from 'app/core/utils/file_export';
|
import * as fileExport from 'app/core/utils/file_export';
|
||||||
import * as flatten from 'app/core/utils/flatten';
|
import * as flatten from 'app/core/utils/flatten';
|
||||||
@@ -160,15 +160,18 @@ export function importPluginModule(path: string): Promise<any> {
|
|||||||
return System.import(path);
|
return System.import(path);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importDataSourcePlugin(path: string): Promise<DataSourcePlugin<any>> {
|
export function importDataSourcePlugin(meta: DataSourcePluginMeta): Promise<DataSourcePlugin<any>> {
|
||||||
return importPluginModule(path).then(pluginExports => {
|
return importPluginModule(meta.module).then(pluginExports => {
|
||||||
if (pluginExports.plugin) {
|
if (pluginExports.plugin) {
|
||||||
return pluginExports.plugin as DataSourcePlugin<any>;
|
const dsPlugin = pluginExports.plugin as DataSourcePlugin<any>;
|
||||||
|
dsPlugin.meta = meta;
|
||||||
|
return dsPlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pluginExports.Datasource) {
|
if (pluginExports.Datasource) {
|
||||||
const dsPlugin = new DataSourcePlugin(pluginExports.Datasource);
|
const dsPlugin = new DataSourcePlugin(pluginExports.Datasource);
|
||||||
dsPlugin.setComponentsFromLegacyExports(pluginExports);
|
dsPlugin.setComponentsFromLegacyExports(pluginExports);
|
||||||
|
dsPlugin.meta = meta;
|
||||||
return dsPlugin;
|
return dsPlugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -178,17 +181,49 @@ export function importDataSourcePlugin(path: string): Promise<DataSourcePlugin<a
|
|||||||
|
|
||||||
export function importAppPlugin(meta: PluginMeta): Promise<AppPlugin> {
|
export function importAppPlugin(meta: PluginMeta): Promise<AppPlugin> {
|
||||||
return importPluginModule(meta.module).then(pluginExports => {
|
return importPluginModule(meta.module).then(pluginExports => {
|
||||||
return new AppPlugin(meta, pluginExports);
|
const plugin = pluginExports.plugin ? (pluginExports.plugin as AppPlugin) : new AppPlugin();
|
||||||
|
plugin.meta = meta;
|
||||||
|
plugin.setComponentsFromLegacyExports(pluginExports);
|
||||||
|
return plugin;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function importPanelPlugin(path: string): Promise<AngularPanelPlugin | PanelPlugin> {
|
import { getPanelPluginNotFound } from '../dashboard/dashgrid/PanelPluginNotFound';
|
||||||
return importPluginModule(path).then(pluginExports => {
|
|
||||||
|
interface PanelCache {
|
||||||
|
[key: string]: PanelPlugin;
|
||||||
|
}
|
||||||
|
const panelCache: PanelCache = {};
|
||||||
|
|
||||||
|
export function importPanelPlugin(id: string): Promise<PanelPlugin> {
|
||||||
|
const loaded = panelCache[id];
|
||||||
|
if (loaded) {
|
||||||
|
return Promise.resolve(loaded);
|
||||||
|
}
|
||||||
|
const meta = config.panels[id];
|
||||||
|
if (!meta) {
|
||||||
|
return Promise.resolve(getPanelPluginNotFound(id));
|
||||||
|
}
|
||||||
|
|
||||||
|
return importPluginModule(meta.module)
|
||||||
|
.then(pluginExports => {
|
||||||
if (pluginExports.plugin) {
|
if (pluginExports.plugin) {
|
||||||
return pluginExports.plugin as PanelPlugin;
|
return pluginExports.plugin as PanelPlugin;
|
||||||
} else {
|
} else if (pluginExports.PanelCtrl) {
|
||||||
return new AngularPanelPlugin(pluginExports.PanelCtrl);
|
const plugin = new PanelPlugin(null);
|
||||||
|
plugin.angularPanelCtrl = pluginExports.PanelCtrl;
|
||||||
|
return plugin;
|
||||||
}
|
}
|
||||||
|
throw new Error('missing export: plugin or PanelCtrl');
|
||||||
|
})
|
||||||
|
.then(plugin => {
|
||||||
|
plugin.meta = meta;
|
||||||
|
return (panelCache[meta.id] = plugin);
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
// TODO, maybe a different error plugin
|
||||||
|
console.log('Error loading panel plugin', err);
|
||||||
|
return getPanelPluginNotFound(id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,9 +3,11 @@ import { importDataSourcePlugin } from './plugin_loader';
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import DefaultVariableQueryEditor from '../templating/DefaultVariableQueryEditor';
|
import DefaultVariableQueryEditor from '../templating/DefaultVariableQueryEditor';
|
||||||
|
import { DataSourcePluginMeta } from '@grafana/ui';
|
||||||
|
import { TemplateSrv } from '../templating/template_srv';
|
||||||
|
|
||||||
async function loadComponent(module) {
|
async function loadComponent(meta: DataSourcePluginMeta) {
|
||||||
const dsPlugin = await importDataSourcePlugin(module);
|
const dsPlugin = await importDataSourcePlugin(meta);
|
||||||
if (dsPlugin.components.VariableQueryEditor) {
|
if (dsPlugin.components.VariableQueryEditor) {
|
||||||
return dsPlugin.components.VariableQueryEditor;
|
return dsPlugin.components.VariableQueryEditor;
|
||||||
} else {
|
} else {
|
||||||
@@ -14,11 +16,11 @@ async function loadComponent(module) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** @ngInject */
|
/** @ngInject */
|
||||||
function variableQueryEditorLoader(templateSrv) {
|
function variableQueryEditorLoader(templateSrv: TemplateSrv) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
link: async (scope, elem) => {
|
link: async (scope, elem) => {
|
||||||
const Component = await loadComponent(scope.currentDatasource.meta.module);
|
const Component = await loadComponent(scope.currentDatasource.meta);
|
||||||
const props = {
|
const props = {
|
||||||
datasource: scope.currentDatasource,
|
datasource: scope.currentDatasource,
|
||||||
query: scope.current.query,
|
query: scope.current.query,
|
||||||
|
|||||||
Reference in New Issue
Block a user