diff --git a/docs/sources/plugins/developing/apps.md b/docs/sources/plugins/developing/apps.md index b20a521bded..37baab0323e 100644 --- a/docs/sources/plugins/developing/apps.md +++ b/docs/sources/plugins/developing/apps.md @@ -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) diff --git a/public/app/features/plugins/built_in_plugins.ts b/public/app/features/plugins/built_in_plugins.ts index e06e6ee6f5c..92a73df8407 100644 --- a/public/app/features/plugins/built_in_plugins.ts +++ b/public/app/features/plugins/built_in_plugins.ts @@ -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; diff --git a/public/app/features/plugins/plugin_loader.test.ts b/public/app/features/plugins/plugin_loader.test.ts deleted file mode 100644 index 809a2f179b2..00000000000 --- a/public/app/features/plugins/plugin_loader.test.ts +++ /dev/null @@ -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); - }); -}); diff --git a/public/app/plugins/app/example-app/ExampleRootPage.tsx b/public/app/plugins/app/example-app/ExampleRootPage.tsx deleted file mode 100644 index d884731730a..00000000000 --- a/public/app/plugins/app/example-app/ExampleRootPage.tsx +++ /dev/null @@ -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 extends PureComponent { - 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 ( -
- QUERY:
{JSON.stringify(query)}
-
- -
{JSON.stringify(meta.jsonData)}
-
- ); - } -} diff --git a/public/app/plugins/app/example-app/README.md b/public/app/plugins/app/example-app/README.md deleted file mode 100644 index 8dc5db52a34..00000000000 --- a/public/app/plugins/app/example-app/README.md +++ /dev/null @@ -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. - diff --git a/public/app/plugins/app/example-app/config/ExamplePage1.tsx b/public/app/plugins/app/example-app/config/ExamplePage1.tsx deleted file mode 100644 index 6b3d229fe04..00000000000 --- a/public/app/plugins/app/example-app/config/ExamplePage1.tsx +++ /dev/null @@ -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> {} - -export class ExamplePage1 extends PureComponent { - constructor(props: Props) { - super(props); - } - - render() { - const { query } = this.props; - - return ( -
- 11111111111111111111111111111111 -
{JSON.stringify(query)}
- 11111111111111111111111111111111 -
- ); - } -} diff --git a/public/app/plugins/app/example-app/config/ExamplePage2.tsx b/public/app/plugins/app/example-app/config/ExamplePage2.tsx deleted file mode 100644 index 8350dfec1c6..00000000000 --- a/public/app/plugins/app/example-app/config/ExamplePage2.tsx +++ /dev/null @@ -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> {} - -export class ExamplePage2 extends PureComponent { - constructor(props: Props) { - super(props); - } - - render() { - const { query } = this.props; - - return ( -
- 22222222222222222222222222222222 -
{JSON.stringify(query)}
- 22222222222222222222222222222222 -
- ); - } -} diff --git a/public/app/plugins/app/example-app/dashboards/stats.json b/public/app/plugins/app/example-app/dashboards/stats.json deleted file mode 100644 index 45e2316e4de..00000000000 --- a/public/app/plugins/app/example-app/dashboards/stats.json +++ /dev/null @@ -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 -} diff --git a/public/app/plugins/app/example-app/dashboards/streaming.json b/public/app/plugins/app/example-app/dashboards/streaming.json deleted file mode 100644 index ec6714f8816..00000000000 --- a/public/app/plugins/app/example-app/dashboards/streaming.json +++ /dev/null @@ -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 -} diff --git a/public/app/plugins/app/example-app/img/logo.png b/public/app/plugins/app/example-app/img/logo.png deleted file mode 100644 index 46099d58095..00000000000 Binary files a/public/app/plugins/app/example-app/img/logo.png and /dev/null differ diff --git a/public/app/plugins/app/example-app/legacy/angular_example_page.html b/public/app/plugins/app/example-app/legacy/angular_example_page.html deleted file mode 100644 index 850b1d22087..00000000000 --- a/public/app/plugins/app/example-app/legacy/angular_example_page.html +++ /dev/null @@ -1,8 +0,0 @@ - - -

- Example Page -

- -

this is in angular

- diff --git a/public/app/plugins/app/example-app/legacy/angular_example_page.ts b/public/app/plugins/app/example-app/legacy/angular_example_page.ts deleted file mode 100644 index 525e58c0ae7..00000000000 --- a/public/app/plugins/app/example-app/legacy/angular_example_page.ts +++ /dev/null @@ -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); - } -} diff --git a/public/app/plugins/app/example-app/legacy/config.html b/public/app/plugins/app/example-app/legacy/config.html deleted file mode 100644 index 162fc16a74c..00000000000 --- a/public/app/plugins/app/example-app/legacy/config.html +++ /dev/null @@ -1,22 +0,0 @@ -

Example Application

- -

-Angular based config: -

- -
-
-
-
- json Data property - -
-
- -
-
-
-
diff --git a/public/app/plugins/app/example-app/legacy/config.ts b/public/app/plugins/app/example-app/legacy/config.ts deleted file mode 100644 index 3d7c1618fbe..00000000000 --- a/public/app/plugins/app/example-app/legacy/config.ts +++ /dev/null @@ -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); - } -} diff --git a/public/app/plugins/app/example-app/module.ts b/public/app/plugins/app/example-app/module.ts deleted file mode 100644 index ff7cf9112b6..00000000000 --- a/public/app/plugins/app/example-app/module.ts +++ /dev/null @@ -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() - .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', - }); diff --git a/public/app/plugins/app/example-app/plugin.json b/public/app/plugins/app/example-app/plugin.json deleted file mode 100644 index e514d4c3296..00000000000 --- a/public/app/plugins/app/example-app/plugin.json +++ /dev/null @@ -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?" - } - ] -} diff --git a/public/app/plugins/app/example-app/types.ts b/public/app/plugins/app/example-app/types.ts deleted file mode 100644 index c3c5bad5e76..00000000000 --- a/public/app/plugins/app/example-app/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface ExampleAppSettings { - customText?: string; - customCheckbox?: boolean; -}