App Plugins: support react pages and tabs (#16586)

This commit is contained in:
Ryan McKinley
2019-05-02 10:15:39 -07:00
committed by GitHub
parent 31ea0122a0
commit 013f1b8d19
29 changed files with 1316 additions and 450 deletions

View File

@@ -0,0 +1,62 @@
import { ComponentClass } from 'react';
import { NavModel } from './navModel';
import { PluginMeta, PluginIncludeType, GrafanaPlugin } from './plugin';
export interface AppRootProps {
meta: AppPluginMeta;
path: string; // The URL path to this page
query: { [s: string]: any }; // The URL query parameters
/**
* Pass the nav model to the container... is there a better way?
*/
onNavChanged: (nav: NavModel) => void;
}
export interface AppPluginMeta extends PluginMeta {
// TODO anything specific to apps?
}
export class AppPlugin extends GrafanaPlugin<AppPluginMeta> {
// Content under: /a/${plugin-id}/*
root?: ComponentClass<AppRootProps>;
rootNav?: NavModel; // Initial navigation model
// Old style pages
angularPages?: { [component: string]: any };
/**
* Set the component displayed under:
* /a/${plugin-id}/*
*/
setRootPage(root: ComponentClass<AppRootProps>, rootNav?: NavModel) {
this.root = root;
this.rootNav = rootNav;
return this;
}
setComponentsFromLegacyExports(pluginExports: any) {
if (pluginExports.ConfigCtrl) {
this.angularConfigCtrl = pluginExports.ConfigCtrl;
}
const { meta } = this;
if (meta && meta.includes) {
for (const include of meta.includes) {
const { type, component } = include;
if (type === PluginIncludeType.page && component) {
const exp = pluginExports[component];
if (!exp) {
console.warn('App Page uses unknown component: ', component, meta);
continue;
}
if (!this.angularPages) {
this.angularPages = {};
}
this.angularPages[component] = exp;
}
}
}
}
}

View File

@@ -25,11 +25,6 @@ export class DataSourcePlugin<TOptions = {}, TQuery extends DataQuery = DataQuer
return this;
}
setConfigCtrl(ConfigCtrl: any) {
this.components.ConfigCtrl = ConfigCtrl;
return this;
}
setQueryCtrl(QueryCtrl: any) {
this.components.QueryCtrl = QueryCtrl;
return this;
@@ -60,14 +55,15 @@ export class DataSourcePlugin<TOptions = {}, TQuery extends DataQuery = DataQuer
return this;
}
setComponentsFromLegacyExports(exports: any) {
this.components.ConfigCtrl = exports.ConfigCtrl;
this.components.QueryCtrl = exports.QueryCtrl;
this.components.AnnotationsQueryCtrl = exports.AnnotationsQueryCtrl;
this.components.ExploreQueryField = exports.ExploreQueryField;
this.components.ExploreStartPage = exports.ExploreStartPage;
this.components.QueryEditor = exports.QueryEditor;
this.components.VariableQueryEditor = exports.VariableQueryEditor;
setComponentsFromLegacyExports(pluginExports: any) {
this.angularConfigCtrl = pluginExports.ConfigCtrl;
this.components.QueryCtrl = pluginExports.QueryCtrl;
this.components.AnnotationsQueryCtrl = pluginExports.AnnotationsQueryCtrl;
this.components.ExploreQueryField = pluginExports.ExploreQueryField;
this.components.ExploreStartPage = pluginExports.ExploreStartPage;
this.components.QueryEditor = pluginExports.QueryEditor;
this.components.VariableQueryEditor = pluginExports.VariableQueryEditor;
}
}
@@ -91,7 +87,6 @@ interface PluginMetaQueryOptions {
export interface DataSourcePluginComponents<TOptions = {}, TQuery extends DataQuery = DataQuery> {
QueryCtrl?: any;
ConfigCtrl?: any;
AnnotationsQueryCtrl?: any;
VariableQueryEditor?: any;
QueryEditor?: ComponentClass<QueryEditorProps<DataSourceApi, TQuery>>;

View File

@@ -2,6 +2,7 @@ export * from './data';
export * from './time';
export * from './panel';
export * from './plugin';
export * from './app';
export * from './datasource';
export * from './theme';
export * from './graph';

View File

@@ -1,3 +1,5 @@
import { ComponentClass } from 'react';
export enum PluginState {
alpha = 'alpha', // Only included it `enable_alpha` is true
beta = 'beta', // Will show a warning banner
@@ -21,8 +23,12 @@ export interface PluginMeta {
module: string;
baseUrl: string;
// Define plugin requirements
dependencies?: PluginDependencies;
// Filled in by the backend
jsonData?: { [str: string]: any };
secureJsonData?: { [str: string]: any };
enabled?: boolean;
defaultNavUrl?: string;
hasUpdate?: boolean;
@@ -30,6 +36,18 @@ export interface PluginMeta {
pinned?: boolean;
}
interface PluginDependencyInfo {
id: string;
name: string;
version: string;
type: PluginType;
}
export interface PluginDependencies {
grafanaVersion: string;
plugins: PluginDependencyInfo[];
}
export enum PluginIncludeType {
dashboard = 'dashboard',
page = 'page',
@@ -44,6 +62,10 @@ export interface PluginInclude {
name: string;
path?: string;
icon?: string;
role?: string; // "Viewer", Admin, editor???
addToNav?: boolean; // Show in the sidebar... only if type=page?
// Angular app pages
component?: string;
}
@@ -69,44 +91,35 @@ export interface PluginMetaInfo {
version: string;
}
export class GrafanaPlugin<T extends PluginMeta> {
export interface PluginConfigTabProps<T extends PluginMeta> {
meta: T;
query: { [s: string]: any }; // The URL query parameters
}
export interface PluginConfigTab<T extends PluginMeta> {
title: string; // Display
icon?: string;
id: string; // Unique, in URL
body: ComponentClass<PluginConfigTabProps<T>>;
}
export class GrafanaPlugin<T extends PluginMeta = PluginMeta> {
// Meta is filled in by the plugin loading system
meta?: T;
// Soon this will also include common config options
}
// Config control (app/datasource)
angularConfigCtrl?: any;
export class AppPlugin extends GrafanaPlugin<PluginMeta> {
angular?: {
ConfigCtrl?: any;
pages: { [component: string]: any };
};
// Show configuration tabs on the plugin page
configTabs?: Array<PluginConfigTab<T>>;
setComponentsFromLegacyExports(pluginExports: any) {
const legacy = {
ConfigCtrl: undefined,
pages: {} as any,
};
if (pluginExports.ConfigCtrl) {
legacy.ConfigCtrl = pluginExports.ConfigCtrl;
this.angular = legacy;
}
const { meta } = this;
if (meta && meta.includes) {
for (const include of meta.includes) {
const { type, component } = include;
if (type === PluginIncludeType.page && component) {
const exp = pluginExports[component];
if (!exp) {
console.warn('App Page uses unknown component: ', component, meta);
continue;
}
legacy.pages[component] = exp;
this.angular = legacy;
}
}
// Tabs on the plugin page
addConfigTab(tab: PluginConfigTab<T>) {
if (!this.configTabs) {
this.configTabs = [];
}
this.configTabs.push(tab);
return this;
}
}