mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
DashboardScene: Skeleton for Panel edit data tabs (#79130)
This commit is contained in:
parent
dfc139b2ff
commit
f6bd390bc1
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
import { IconName } from '@grafana/data';
|
||||
import { SceneObjectBase, SceneComponentProps } from '@grafana/scenes';
|
||||
|
||||
import { PanelDataPaneTabState, PanelDataPaneTab } from './types';
|
||||
|
||||
export class PanelDataAlertingTab extends SceneObjectBase<PanelDataPaneTabState> implements PanelDataPaneTab {
|
||||
static Component = PanelDataAlertingTabRendered;
|
||||
tabId = 'alert';
|
||||
icon: IconName = 'bell';
|
||||
|
||||
getTabLabel() {
|
||||
return 'Alert';
|
||||
}
|
||||
}
|
||||
|
||||
function PanelDataAlertingTabRendered(props: SceneComponentProps<PanelDataAlertingTab>) {
|
||||
return <div>TODO Alerting</div>;
|
||||
}
|
@ -0,0 +1,128 @@
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
SceneComponentProps,
|
||||
SceneObjectBase,
|
||||
SceneObjectRef,
|
||||
SceneObjectState,
|
||||
SceneObjectUrlSyncConfig,
|
||||
SceneObjectUrlValues,
|
||||
VizPanel,
|
||||
} from '@grafana/scenes';
|
||||
import { Tab, TabContent, TabsBar } from '@grafana/ui';
|
||||
import { shouldShowAlertingTab } from 'app/features/dashboard/components/PanelEditor/state/selectors';
|
||||
|
||||
import { PanelDataAlertingTab } from './PanelDataAlertingTab';
|
||||
import { PanelDataQueriesTab } from './PanelDataQueriesTab';
|
||||
import { PanelDataTransformationsTab } from './PanelDataTransformationsTab';
|
||||
import { PanelDataPaneTab } from './types';
|
||||
|
||||
export interface PanelDataPaneState extends SceneObjectState {
|
||||
panelRef: SceneObjectRef<VizPanel>;
|
||||
tabs?: PanelDataPaneTab[];
|
||||
tab?: string;
|
||||
}
|
||||
|
||||
export class PanelDataPane extends SceneObjectBase<PanelDataPaneState> {
|
||||
static Component = PanelDataPaneRendered;
|
||||
protected _urlSync = new SceneObjectUrlSyncConfig(this, { keys: ['tab'] });
|
||||
|
||||
getUrlState() {
|
||||
return {
|
||||
tab: this.state.tab,
|
||||
};
|
||||
}
|
||||
|
||||
updateFromUrl(values: SceneObjectUrlValues) {
|
||||
if (!values.tab) {
|
||||
return;
|
||||
}
|
||||
if (typeof values.tab === 'string') {
|
||||
this.setState({ tab: values.tab });
|
||||
}
|
||||
}
|
||||
|
||||
constructor(state: Omit<PanelDataPaneState, 'tab'> & { tab?: string }) {
|
||||
super({
|
||||
tab: 'queries',
|
||||
...state,
|
||||
});
|
||||
|
||||
const { panelRef } = this.state;
|
||||
const panel = panelRef.resolve();
|
||||
|
||||
if (panel) {
|
||||
// The subscription below is needed because the plugin may not be loaded when this pane is mounted.
|
||||
// This can happen i.e. when the user opens the panel editor directly via an URL.
|
||||
this._subs.add(
|
||||
panel.subscribeToState((n, p) => {
|
||||
if (n.pluginVersion || p.pluginId !== n.pluginId) {
|
||||
this.buildTabs();
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
this.addActivationHandler(() => this.buildTabs());
|
||||
}
|
||||
|
||||
private buildTabs() {
|
||||
const { panelRef } = this.state;
|
||||
const tabs: PanelDataPaneTab[] = [];
|
||||
|
||||
if (panelRef) {
|
||||
const plugin = panelRef.resolve().getPlugin();
|
||||
if (!plugin) {
|
||||
this.setState({ tabs });
|
||||
return;
|
||||
}
|
||||
if (plugin.meta.skipDataQuery) {
|
||||
this.setState({ tabs });
|
||||
return;
|
||||
} else {
|
||||
tabs.push(new PanelDataQueriesTab({}));
|
||||
tabs.push(new PanelDataTransformationsTab({}));
|
||||
|
||||
if (shouldShowAlertingTab(plugin)) {
|
||||
tabs.push(new PanelDataAlertingTab({}));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({ tabs });
|
||||
}
|
||||
|
||||
onChangeTab = (tab: PanelDataPaneTab) => {
|
||||
this.setState({ tab: tab.tabId });
|
||||
};
|
||||
}
|
||||
|
||||
function PanelDataPaneRendered({ model }: SceneComponentProps<PanelDataPane>) {
|
||||
const { tab, tabs } = model.useState();
|
||||
|
||||
if (!tabs) {
|
||||
return;
|
||||
}
|
||||
|
||||
const currentTab = tabs.find((t) => t.tabId === tab);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<TabsBar hideBorder={true}>
|
||||
{tabs.map((t, index) => {
|
||||
return (
|
||||
<Tab
|
||||
key={`${t.getTabLabel()}-${index}`}
|
||||
label={t.getTabLabel()}
|
||||
icon={t.icon}
|
||||
// suffix={}
|
||||
active={t.tabId === tab}
|
||||
onChangeTab={() => model.onChangeTab(t)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</TabsBar>
|
||||
<TabContent>{currentTab && <currentTab.Component model={currentTab} />}</TabContent>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
import { IconName } from '@grafana/data';
|
||||
import { SceneObjectBase, SceneComponentProps } from '@grafana/scenes';
|
||||
|
||||
import { PanelDataPaneTabState, PanelDataPaneTab } from './types';
|
||||
|
||||
export class PanelDataQueriesTab extends SceneObjectBase<PanelDataPaneTabState> implements PanelDataPaneTab {
|
||||
static Component = PanelDataQueriesTabRendered;
|
||||
tabId = 'queries';
|
||||
icon: IconName = 'database';
|
||||
|
||||
getTabLabel() {
|
||||
return 'Queries';
|
||||
}
|
||||
}
|
||||
|
||||
function PanelDataQueriesTabRendered(props: SceneComponentProps<PanelDataQueriesTab>) {
|
||||
return <div>TODO Queries</div>;
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
import React from 'react';
|
||||
|
||||
import { IconName } from '@grafana/data';
|
||||
import { SceneObjectBase, SceneComponentProps } from '@grafana/scenes';
|
||||
|
||||
import { PanelDataPaneTabState, PanelDataPaneTab } from './types';
|
||||
|
||||
export class PanelDataTransformationsTab extends SceneObjectBase<PanelDataPaneTabState> implements PanelDataPaneTab {
|
||||
static Component = PanelDataTransformationsTabRendered;
|
||||
tabId = 'transformations';
|
||||
icon: IconName = 'process';
|
||||
|
||||
getTabLabel() {
|
||||
return 'Transformations';
|
||||
}
|
||||
}
|
||||
|
||||
function PanelDataTransformationsTabRendered(props: SceneComponentProps<PanelDataTransformationsTab>) {
|
||||
return <div>TODO Transformations</div>;
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
import { IconName } from '@grafana/data';
|
||||
import { SceneObject, SceneObjectState } from '@grafana/scenes';
|
||||
|
||||
export interface PanelDataPaneTabState extends SceneObjectState {}
|
||||
|
||||
export interface PanelDataPaneTab extends SceneObject {
|
||||
getTabLabel(): string;
|
||||
tabId: string;
|
||||
icon: IconName;
|
||||
}
|
@ -4,6 +4,7 @@ import { NavIndex } from '@grafana/data';
|
||||
import { locationService } from '@grafana/runtime';
|
||||
import {
|
||||
getUrlSyncManager,
|
||||
SceneFlexItem,
|
||||
SceneFlexLayout,
|
||||
SceneObject,
|
||||
SceneObjectBase,
|
||||
@ -17,6 +18,7 @@ import {
|
||||
import { DashboardScene } from '../scene/DashboardScene';
|
||||
import { getDashboardUrl } from '../utils/urlBuilders';
|
||||
|
||||
import { PanelDataPane } from './PanelDataPane/PanelDataPane';
|
||||
import { PanelEditorRenderer } from './PanelEditorRenderer';
|
||||
import { PanelOptionsPane } from './PanelOptionsPane';
|
||||
import { PanelVizTypePicker } from './PanelVizTypePicker';
|
||||
@ -124,9 +126,15 @@ export function buildPanelEditScene(dashboard: DashboardScene, panel: VizPanel):
|
||||
$timeRange: dashboardStateCloned.$timeRange,
|
||||
body: new SplitLayout({
|
||||
direction: 'row',
|
||||
primary: new SceneFlexLayout({
|
||||
primary: new SplitLayout({
|
||||
direction: 'column',
|
||||
children: [vizPanelMgr],
|
||||
primary: new SceneFlexLayout({
|
||||
direction: 'column',
|
||||
children: [panelClone],
|
||||
}),
|
||||
secondary: new SceneFlexItem({
|
||||
body: new PanelDataPane({ panelRef: panelClone.getRef() }),
|
||||
}),
|
||||
}),
|
||||
secondary: new SceneFlexLayout({
|
||||
direction: 'column',
|
||||
|
@ -39,14 +39,7 @@ export const getPanelEditorTabs = memoizeOne((tab?: string, plugin?: PanelPlugin
|
||||
});
|
||||
}
|
||||
|
||||
const { alertingEnabled, unifiedAlertingEnabled } = getConfig();
|
||||
const hasRuleReadPermissions = contextSrv.hasPermission(getRulesPermissions(GRAFANA_RULES_SOURCE_NAME).read);
|
||||
const isAlertingAvailable = alertingEnabled || (unifiedAlertingEnabled && hasRuleReadPermissions);
|
||||
|
||||
const isGraph = plugin.meta.id === 'graph';
|
||||
const isTimeseries = plugin.meta.id === 'timeseries';
|
||||
|
||||
if ((isAlertingAvailable && isGraph) || isTimeseries) {
|
||||
if (shouldShowAlertingTab(plugin)) {
|
||||
tabs.push({
|
||||
id: PanelEditorTabId.Alert,
|
||||
text: 'Alert',
|
||||
@ -60,3 +53,14 @@ export const getPanelEditorTabs = memoizeOne((tab?: string, plugin?: PanelPlugin
|
||||
|
||||
return tabs;
|
||||
});
|
||||
|
||||
export function shouldShowAlertingTab(plugin: PanelPlugin) {
|
||||
const { alertingEnabled, unifiedAlertingEnabled } = getConfig();
|
||||
const hasRuleReadPermissions = contextSrv.hasPermission(getRulesPermissions(GRAFANA_RULES_SOURCE_NAME).read);
|
||||
const isAlertingAvailable = alertingEnabled || (unifiedAlertingEnabled && hasRuleReadPermissions);
|
||||
|
||||
const isGraph = plugin.meta.id === 'graph';
|
||||
const isTimeseries = plugin.meta.id === 'timeseries';
|
||||
|
||||
return (isAlertingAvailable && isGraph) || isTimeseries;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user