mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
AppPlugin: remove simple app from the core repo (#21526)
This commit is contained in:
parent
f6130db03d
commit
c5da1864a6
@ -55,5 +55,5 @@ If possible a link to a dashboard or custom page should be shown after enabling
|
||||
|
||||
> Our goal is not to have a very extensive documentation but rather have actual
|
||||
> code that people can look at. An example implementation of an app can be found
|
||||
> in this [example app repo](https://github.com/grafana/grafana/tree/master/public/app/plugins/app/example-app)
|
||||
> in this [example app repo](https://github.com/grafana/simple-app-plugin)
|
||||
|
||||
|
@ -54,8 +54,6 @@ import * as barGaugePanel from 'app/plugins/panel/bargauge/module';
|
||||
import * as logsPanel from 'app/plugins/panel/logs/module';
|
||||
import * as newsPanel from 'app/plugins/panel/news/module';
|
||||
|
||||
const exampleApp = async () => await import(/* webpackChunkName: "exampleApp" */ 'app/plugins/app/example-app/module');
|
||||
|
||||
const builtInPlugins: any = {
|
||||
'app/plugins/datasource/graphite/module': graphitePlugin,
|
||||
'app/plugins/datasource/cloudwatch/module': cloudwatchPlugin,
|
||||
@ -94,8 +92,6 @@ const builtInPlugins: any = {
|
||||
'app/plugins/panel/piechart/module': pieChartPanel,
|
||||
'app/plugins/panel/bargauge/module': barGaugePanel,
|
||||
'app/plugins/panel/logs/module': logsPanel,
|
||||
|
||||
'app/plugins/app/example-app/module': exampleApp,
|
||||
};
|
||||
|
||||
export default builtInPlugins;
|
||||
|
@ -1,110 +0,0 @@
|
||||
// Use the real plugin_loader (stubbed by default)
|
||||
jest.unmock('app/features/plugins/plugin_loader');
|
||||
|
||||
(global as any).ace = {
|
||||
define: jest.fn(),
|
||||
};
|
||||
|
||||
jest.mock('app/core/core', () => {
|
||||
return {
|
||||
coreModule: {
|
||||
directive: jest.fn(),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
import { SystemJS } from '@grafana/runtime';
|
||||
import { AppPluginMeta, PluginMetaInfo, PluginType, PluginIncludeType, AppPlugin } from '@grafana/data';
|
||||
import { importAppPlugin } from './plugin_loader';
|
||||
|
||||
class MyCustomApp extends AppPlugin {
|
||||
initWasCalled = false;
|
||||
calledTwice = false;
|
||||
|
||||
init(meta: AppPluginMeta) {
|
||||
this.initWasCalled = true;
|
||||
this.calledTwice = this.meta === meta;
|
||||
}
|
||||
}
|
||||
|
||||
describe('Load App', () => {
|
||||
const app = new MyCustomApp();
|
||||
const modulePath = 'my/custom/plugin/module';
|
||||
|
||||
beforeAll(() => {
|
||||
SystemJS.set(modulePath, SystemJS.newModule({ plugin: app }));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
SystemJS.delete(modulePath);
|
||||
});
|
||||
|
||||
it('should call init and set meta', async () => {
|
||||
const meta: AppPluginMeta = {
|
||||
id: 'test-app',
|
||||
module: modulePath,
|
||||
baseUrl: 'xxx',
|
||||
info: {} as PluginMetaInfo,
|
||||
type: PluginType.app,
|
||||
name: 'test',
|
||||
};
|
||||
|
||||
// Check that we mocked the import OK
|
||||
const m = await SystemJS.import(modulePath);
|
||||
expect(m.plugin).toBe(app);
|
||||
|
||||
const loaded = await importAppPlugin(meta);
|
||||
expect(loaded).toBe(app);
|
||||
expect(app.meta).toBe(meta);
|
||||
expect(app.initWasCalled).toBeTruthy();
|
||||
expect(app.calledTwice).toBeFalsy();
|
||||
|
||||
const again = await importAppPlugin(meta);
|
||||
expect(again).toBe(app);
|
||||
expect(app.calledTwice).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
import { ExampleConfigCtrl as ConfigCtrl } from 'app/plugins/app/example-app/legacy/config';
|
||||
import { AngularExamplePageCtrl } from 'app/plugins/app/example-app/legacy/angular_example_page';
|
||||
|
||||
describe('Load Legacy App', () => {
|
||||
const app = {
|
||||
ConfigCtrl,
|
||||
AngularExamplePageCtrl, // Must match `pages.component` in plugin.json
|
||||
};
|
||||
|
||||
const modulePath = 'my/custom/legacy/plugin/module';
|
||||
|
||||
beforeAll(() => {
|
||||
SystemJS.set(modulePath, SystemJS.newModule(app));
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
SystemJS.delete(modulePath);
|
||||
});
|
||||
|
||||
it('should call init and set meta for legacy app', async () => {
|
||||
const meta: AppPluginMeta = {
|
||||
id: 'test-app',
|
||||
module: modulePath,
|
||||
baseUrl: 'xxx',
|
||||
info: {} as PluginMetaInfo,
|
||||
type: PluginType.app,
|
||||
name: 'test',
|
||||
includes: [
|
||||
{
|
||||
type: PluginIncludeType.page,
|
||||
name: 'Example Page',
|
||||
component: 'AngularExamplePageCtrl',
|
||||
role: 'Viewer',
|
||||
addToNav: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const loaded = await importAppPlugin(meta);
|
||||
expect(loaded).toHaveProperty('angularPages');
|
||||
expect(loaded.angularPages).toHaveProperty('AngularExamplePageCtrl', AngularExamplePageCtrl);
|
||||
});
|
||||
});
|
@ -1,103 +0,0 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
// Types
|
||||
import { NavModelItem, AppRootProps } from '@grafana/data';
|
||||
|
||||
interface Props extends AppRootProps {}
|
||||
|
||||
const TAB_ID_A = 'A';
|
||||
const TAB_ID_B = 'B';
|
||||
const TAB_ID_C = 'C';
|
||||
|
||||
export class ExampleRootPage<ExampleAppSettings> extends PureComponent<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.updateNav();
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Props) {
|
||||
if (this.props.query !== prevProps.query) {
|
||||
if (this.props.query.tab !== prevProps.query.tab) {
|
||||
this.updateNav();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateNav() {
|
||||
const { path, onNavChanged, query, meta } = this.props;
|
||||
|
||||
const tabs: NavModelItem[] = [];
|
||||
tabs.push({
|
||||
text: 'Tab A',
|
||||
icon: 'fa fa-fw fa-file-text-o',
|
||||
url: path + '?tab=' + TAB_ID_A,
|
||||
id: TAB_ID_A,
|
||||
});
|
||||
tabs.push({
|
||||
text: 'Tab B',
|
||||
icon: 'fa fa-fw fa-file-text-o',
|
||||
url: path + '?tab=' + TAB_ID_B,
|
||||
id: TAB_ID_B,
|
||||
});
|
||||
tabs.push({
|
||||
text: 'Tab C',
|
||||
icon: 'fa fa-fw fa-file-text-o',
|
||||
url: path + '?tab=' + TAB_ID_C,
|
||||
id: TAB_ID_C,
|
||||
});
|
||||
|
||||
// Set the active tab
|
||||
let found = false;
|
||||
const selected = query.tab || TAB_ID_B;
|
||||
for (const tab of tabs) {
|
||||
tab.active = !found && selected === tab.id;
|
||||
if (tab.active) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
tabs[0].active = true;
|
||||
}
|
||||
|
||||
const node = {
|
||||
text: 'This is the Page title',
|
||||
img: meta.info.logos.large,
|
||||
subTitle: 'subtitle here',
|
||||
url: path,
|
||||
children: tabs,
|
||||
};
|
||||
|
||||
// Update the page header
|
||||
onNavChanged({
|
||||
node: node,
|
||||
main: node,
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
const { path, query, meta } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
QUERY: <pre>{JSON.stringify(query)}</pre>
|
||||
<br />
|
||||
<ul>
|
||||
<li>
|
||||
<a href={path + '?x=1'}>111</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={path + '?x=AAA'}>AAA</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={path + '?x=1&y=2&y=3'}>ZZZ</a>
|
||||
</li>
|
||||
</ul>
|
||||
<pre>{JSON.stringify(meta.jsonData)}</pre>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
# Example App - Native Plugin
|
||||
|
||||
This is an example app. It has no real use other than making sure external apps are supported.
|
||||
|
@ -1,26 +0,0 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
// Types
|
||||
import { PluginConfigPageProps, AppPluginMeta } from '@grafana/data';
|
||||
import { ExampleAppSettings } from '../types';
|
||||
|
||||
interface Props extends PluginConfigPageProps<AppPluginMeta<ExampleAppSettings>> {}
|
||||
|
||||
export class ExamplePage1 extends PureComponent<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { query } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
11111111111111111111111111111111
|
||||
<pre>{JSON.stringify(query)}</pre>
|
||||
11111111111111111111111111111111
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
// Libraries
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
// Types
|
||||
import { PluginConfigPageProps, AppPluginMeta } from '@grafana/data';
|
||||
import { ExampleAppSettings } from '../types';
|
||||
|
||||
interface Props extends PluginConfigPageProps<AppPluginMeta<ExampleAppSettings>> {}
|
||||
|
||||
export class ExamplePage2 extends PureComponent<Props> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
render() {
|
||||
const { query } = this.props;
|
||||
|
||||
return (
|
||||
<div>
|
||||
22222222222222222222222222222222
|
||||
<pre>{JSON.stringify(query)}</pre>
|
||||
22222222222222222222222222222222
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,110 +0,0 @@
|
||||
{
|
||||
"__inputs": [],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "6.2.0-pre"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "singlestat2",
|
||||
"name": "Singlestat (react)",
|
||||
"version": ""
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"gridPos": {
|
||||
"h": 4,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"options": {
|
||||
"orientation": "auto",
|
||||
"sparkline": {
|
||||
"fillColor": "rgba(31, 118, 189, 0.18)",
|
||||
"full": false,
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": true
|
||||
},
|
||||
"thresholds": [
|
||||
{
|
||||
"color": "green",
|
||||
"index": 0,
|
||||
"value": null
|
||||
},
|
||||
{
|
||||
"color": "red",
|
||||
"index": 1,
|
||||
"value": 80
|
||||
}
|
||||
],
|
||||
"valueMappings": [],
|
||||
"valueOptions": {
|
||||
"decimals": null,
|
||||
"prefix": "",
|
||||
"stat": "mean",
|
||||
"suffix": "",
|
||||
"unit": "none"
|
||||
}
|
||||
},
|
||||
"pluginVersion": "6.2.0-pre",
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"scenarioId": "random_walk_table",
|
||||
"stringInput": ""
|
||||
},
|
||||
{
|
||||
"refId": "B",
|
||||
"scenarioId": "random_walk_table",
|
||||
"stringInput": ""
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Panel Title",
|
||||
"type": "singlestat2"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 18,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-6h",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
|
||||
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "stats",
|
||||
"uid": "YeBxHjzWz",
|
||||
"version": 1
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
{
|
||||
"__inputs": [],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "6.2.0-pre"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "graph2",
|
||||
"name": "React Graph",
|
||||
"version": ""
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": "-- Grafana --",
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
}
|
||||
]
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"description": "",
|
||||
"gridPos": {
|
||||
"h": 6,
|
||||
"w": 24,
|
||||
"x": 0,
|
||||
"y": 0
|
||||
},
|
||||
"id": 2,
|
||||
"links": [],
|
||||
"targets": [
|
||||
{
|
||||
"refId": "A",
|
||||
"scenarioId": "streaming_client",
|
||||
"stream": {
|
||||
"noise": 10,
|
||||
"speed": 100,
|
||||
"spread": 20,
|
||||
"type": "signal"
|
||||
},
|
||||
"stringInput": ""
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Simple dummy streaming example",
|
||||
"type": "graph2"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 18,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1m",
|
||||
"to": "now"
|
||||
},
|
||||
"timepicker": {
|
||||
"refresh_intervals": ["5s", "10s", "30s", "1m", "5m", "15m", "30m", "1h", "2h", "1d"],
|
||||
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
|
||||
},
|
||||
"timezone": "",
|
||||
"title": "simple streaming",
|
||||
"uid": "TbbEZjzWz",
|
||||
"version": 1
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 42 KiB |
@ -1,8 +0,0 @@
|
||||
|
||||
|
||||
<h3 class="page-heading">
|
||||
Example Page
|
||||
</h3>
|
||||
|
||||
<p>this is in angular</p>
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { GrafanaRootScope } from 'app/routes/GrafanaCtrl';
|
||||
|
||||
export class AngularExamplePageCtrl {
|
||||
static templateUrl = 'legacy/angular_example_page.html';
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope: any, $rootScope: GrafanaRootScope) {
|
||||
console.log('AngularExamplePageCtrl:', this);
|
||||
}
|
||||
}
|
@ -1,22 +0,0 @@
|
||||
<h2>Example Application</h2>
|
||||
|
||||
<p>
|
||||
Angular based config:
|
||||
</p>
|
||||
|
||||
<div class="gf-form">
|
||||
<div class="gf-form-group">
|
||||
<div class="gf-form-inline">
|
||||
<div class="gf-form">
|
||||
<span class="gf-form-label">json Data property</span>
|
||||
<input type="text" class="gf-form-input" ng-model="ctrl.appModel.jsonData.customText" >
|
||||
</div>
|
||||
<div class="gf-form">
|
||||
<gf-form-checkbox class="gf-form"
|
||||
label="Custom Value"
|
||||
checked="ctrl.appModel.jsonData.customCheckbox"
|
||||
switch-class="max-width-6"></gf-form-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
@ -1,36 +0,0 @@
|
||||
import { PluginMeta } from '@grafana/data';
|
||||
|
||||
export class ExampleConfigCtrl {
|
||||
static templateUrl = 'legacy/config.html';
|
||||
|
||||
appEditCtrl: any;
|
||||
appModel: PluginMeta;
|
||||
|
||||
/** @ngInject */
|
||||
constructor($scope: any, $injector: any) {
|
||||
this.appEditCtrl.setPostUpdateHook(this.postUpdate.bind(this));
|
||||
|
||||
// Make sure it has a JSON Data spot
|
||||
if (!this.appModel) {
|
||||
this.appModel = {} as PluginMeta;
|
||||
}
|
||||
|
||||
// Required until we get the types sorted on appModel :(
|
||||
const appModel = this.appModel as any;
|
||||
if (!appModel.jsonData) {
|
||||
appModel.jsonData = {};
|
||||
}
|
||||
|
||||
console.log('ExampleConfigCtrl', this);
|
||||
}
|
||||
|
||||
postUpdate() {
|
||||
if (!this.appModel.enabled) {
|
||||
console.log('Not enabled...');
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO, can do stuff after update
|
||||
console.log('Post Update:', this);
|
||||
}
|
||||
}
|
@ -1,29 +0,0 @@
|
||||
// Angular pages
|
||||
import { ExampleConfigCtrl } from './legacy/config';
|
||||
import { AngularExamplePageCtrl } from './legacy/angular_example_page';
|
||||
import { AppPlugin } from '@grafana/data';
|
||||
import { ExamplePage1 } from './config/ExamplePage1';
|
||||
import { ExamplePage2 } from './config/ExamplePage2';
|
||||
import { ExampleRootPage } from './ExampleRootPage';
|
||||
import { ExampleAppSettings } from './types';
|
||||
|
||||
// Legacy exports just for testing
|
||||
export {
|
||||
ExampleConfigCtrl as ConfigCtrl,
|
||||
AngularExamplePageCtrl, // Must match `pages.component` in plugin.json
|
||||
};
|
||||
|
||||
export const plugin = new AppPlugin<ExampleAppSettings>()
|
||||
.setRootPage(ExampleRootPage)
|
||||
.addConfigPage({
|
||||
title: 'Page 1',
|
||||
icon: 'fa fa-info',
|
||||
body: ExamplePage1,
|
||||
id: 'page1',
|
||||
})
|
||||
.addConfigPage({
|
||||
title: 'Page 2',
|
||||
icon: 'fa fa-user',
|
||||
body: ExamplePage2,
|
||||
id: 'page2',
|
||||
});
|
@ -1,42 +0,0 @@
|
||||
{
|
||||
"type": "app",
|
||||
"name": "Example App",
|
||||
"id": "example-app",
|
||||
"state": "alpha",
|
||||
|
||||
"info": {
|
||||
"author": {
|
||||
"name": "Grafana Project",
|
||||
"url": "https://grafana.com"
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/logo.png",
|
||||
"large": "img/logo.png"
|
||||
}
|
||||
},
|
||||
|
||||
"includes": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "Angular Page",
|
||||
"component": "AngularExamplePageCtrl",
|
||||
"role": "Viewer",
|
||||
"addToNav": true,
|
||||
"defaultNav": true
|
||||
},
|
||||
{
|
||||
"type": "dashboard",
|
||||
"name": "Streaming Example",
|
||||
"path": "dashboards/streaming.json"
|
||||
},
|
||||
{
|
||||
"type": "dashboard",
|
||||
"name": "Lots of Stats",
|
||||
"path": "dashboards/stats.json"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"name": "Anything -- just display?"
|
||||
}
|
||||
]
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
export interface ExampleAppSettings {
|
||||
customText?: string;
|
||||
customCheckbox?: boolean;
|
||||
}
|
Loading…
Reference in New Issue
Block a user