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,102 @@
// Libraries
import React, { PureComponent } from 'react';
// Types
import { AppRootProps, NavModelItem } from '@grafana/ui';
interface Props extends AppRootProps {}
const TAB_ID_A = 'A';
const TAB_ID_B = 'B';
const TAB_ID_C = 'C';
export class ExampleRootPage 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 } = 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>
</div>
);
}
}

View File

@@ -0,0 +1,25 @@
// Libraries
import React, { PureComponent } from 'react';
// Types
import { PluginConfigTabProps, AppPluginMeta } from '@grafana/ui';
interface Props extends PluginConfigTabProps<AppPluginMeta> {}
export class ExampleTab1 extends PureComponent<Props> {
constructor(props: Props) {
super(props);
}
render() {
const { query } = this.props;
return (
<div>
11111111111111111111111111111111
<pre>{JSON.stringify(query)}</pre>
11111111111111111111111111111111
</div>
);
}
}

View File

@@ -0,0 +1,25 @@
// Libraries
import React, { PureComponent } from 'react';
// Types
import { PluginConfigTabProps, AppPluginMeta } from '@grafana/ui';
interface Props extends PluginConfigTabProps<AppPluginMeta> {}
export class ExampleTab2 extends PureComponent<Props> {
constructor(props: Props) {
super(props);
}
render() {
const { query } = this.props;
return (
<div>
22222222222222222222222222222222
<pre>{JSON.stringify(query)}</pre>
22222222222222222222222222222222
</div>
);
}
}

View File

@@ -0,0 +1,110 @@
{
"__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
}

View File

@@ -0,0 +1,83 @@
{
"__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
}

View File

@@ -1,9 +1,28 @@
// Angular pages
import { ExampleConfigCtrl } from './legacy/config';
import { AngularExamplePageCtrl } from './legacy/angular_example_page';
import { AppPlugin } from '@grafana/ui';
import { ExampleTab1 } from './config/ExampleTab1';
import { ExampleTab2 } from './config/ExampleTab2';
import { ExampleRootPage } from './ExampleRootPage';
// Legacy exports just for testing
export {
ExampleConfigCtrl as ConfigCtrl,
// Must match `pages.component` in plugin.json
AngularExamplePageCtrl,
AngularExamplePageCtrl, // Must match `pages.component` in plugin.json
};
export const plugin = new AppPlugin()
.setRootPage(ExampleRootPage)
.addConfigTab({
title: 'Tab 1',
icon: 'fa fa-info',
body: ExampleTab1,
id: 'tab1',
})
.addConfigTab({
title: 'Tab 2',
icon: 'fa fa-user',
body: ExampleTab2,
id: 'tab2',
});

View File

@@ -23,6 +23,20 @@
"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?"
}
]
}