2018-10-14 08:39:34 -05:00
|
|
|
// Libraries
|
2020-12-17 04:45:28 -06:00
|
|
|
import React, { Component } from 'react';
|
2019-05-06 08:26:09 -05:00
|
|
|
import classNames from 'classnames';
|
2020-12-23 03:45:31 -06:00
|
|
|
import { Subscription } from 'rxjs';
|
2018-10-14 08:39:34 -05:00
|
|
|
// Components
|
2018-10-25 05:47:09 -05:00
|
|
|
import { PanelHeader } from './PanelHeader/PanelHeader';
|
2021-05-06 14:22:03 -05:00
|
|
|
import { ErrorBoundary, PanelContextProvider, PanelContext, SeriesVisibilityChangeMode } from '@grafana/ui';
|
2019-05-06 08:26:09 -05:00
|
|
|
// Utils & Services
|
|
|
|
import { getTimeSrv, TimeSrv } from '../services/TimeSrv';
|
2019-11-25 15:26:18 -06:00
|
|
|
import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel';
|
2019-02-01 01:12:58 -06:00
|
|
|
import { profiler } from 'app/core/profiler';
|
2019-03-12 08:24:48 -05:00
|
|
|
import config from 'app/core/config';
|
2018-10-14 08:39:34 -05:00
|
|
|
// Types
|
2019-01-31 07:16:06 -06:00
|
|
|
import { DashboardModel, PanelModel } from '../state';
|
2019-11-25 15:26:18 -06:00
|
|
|
import { PANEL_BORDER } from 'app/core/constants';
|
2019-10-31 04:48:05 -05:00
|
|
|
import {
|
|
|
|
AbsoluteTimeRange,
|
2021-05-10 07:24:23 -05:00
|
|
|
DashboardCursorSync,
|
2021-04-30 15:33:29 -05:00
|
|
|
EventBusSrv,
|
|
|
|
EventFilterOptions,
|
2021-01-07 04:26:56 -06:00
|
|
|
FieldConfigSource,
|
|
|
|
getDefaultTimeRange,
|
|
|
|
LoadingState,
|
2019-10-31 04:48:05 -05:00
|
|
|
PanelData,
|
|
|
|
PanelPlugin,
|
2020-08-20 01:52:26 -05:00
|
|
|
PanelPluginMeta,
|
2021-01-07 04:26:56 -06:00
|
|
|
toDataFrameDTO,
|
|
|
|
toUtc,
|
2019-10-31 04:48:05 -05:00
|
|
|
} from '@grafana/data';
|
2020-05-11 04:46:57 -05:00
|
|
|
import { selectors } from '@grafana/e2e-selectors';
|
2020-11-17 03:25:36 -06:00
|
|
|
import { loadSnapshotData } from '../utils/loadSnapshotData';
|
2020-12-23 03:45:31 -06:00
|
|
|
import { RefreshEvent, RenderEvent } from 'app/types/events';
|
2021-05-03 10:23:17 -05:00
|
|
|
import { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/overrides/colorSeriesConfigFactory';
|
2021-05-06 14:22:03 -05:00
|
|
|
import { seriesVisibilityConfigFactory } from './SeriesVisibilityConfigFactory';
|
2018-06-19 07:51:57 -05:00
|
|
|
|
2019-02-12 09:06:02 -06:00
|
|
|
const DEFAULT_PLUGIN_ERROR = 'Error in plugin';
|
2019-01-14 07:47:41 -06:00
|
|
|
|
2018-11-06 09:37:51 -06:00
|
|
|
export interface Props {
|
2018-06-19 07:51:57 -05:00
|
|
|
panel: PanelModel;
|
|
|
|
dashboard: DashboardModel;
|
2019-05-01 00:36:46 -05:00
|
|
|
plugin: PanelPlugin;
|
2020-04-10 09:37:26 -05:00
|
|
|
isViewing: boolean;
|
2020-07-09 08:16:35 -05:00
|
|
|
isEditing: boolean;
|
2019-05-03 08:35:37 -05:00
|
|
|
isInView: boolean;
|
2019-04-17 12:51:50 -05:00
|
|
|
width: number;
|
|
|
|
height: number;
|
2018-06-19 07:51:57 -05:00
|
|
|
}
|
|
|
|
|
2018-11-06 09:37:51 -06:00
|
|
|
export interface State {
|
2019-04-17 12:51:50 -05:00
|
|
|
isFirstLoad: boolean;
|
2018-11-05 10:46:09 -06:00
|
|
|
renderCounter: number;
|
2020-02-17 00:25:27 -06:00
|
|
|
errorMessage?: string;
|
2019-05-03 08:35:37 -05:00
|
|
|
refreshWhenInView: boolean;
|
2021-04-30 15:33:29 -05:00
|
|
|
context: PanelContext;
|
2019-04-17 12:51:50 -05:00
|
|
|
data: PanelData;
|
2018-10-14 08:39:34 -05:00
|
|
|
}
|
2018-07-05 15:10:39 -05:00
|
|
|
|
2020-12-17 04:45:28 -06:00
|
|
|
export class PanelChrome extends Component<Props, State> {
|
2020-12-23 03:45:31 -06:00
|
|
|
private readonly timeSrv: TimeSrv = getTimeSrv();
|
|
|
|
private subs = new Subscription();
|
2021-04-30 15:33:29 -05:00
|
|
|
private eventFilter: EventFilterOptions = { onlyLocal: true };
|
2018-11-08 07:44:12 -06:00
|
|
|
|
2019-04-17 12:51:50 -05:00
|
|
|
constructor(props: Props) {
|
2018-06-19 07:51:57 -05:00
|
|
|
super(props);
|
2020-02-17 00:25:27 -06:00
|
|
|
|
2021-04-30 15:33:29 -05:00
|
|
|
// Can this eventBus be on PanelModel?
|
|
|
|
// when we have more complex event filtering, that may be a better option
|
|
|
|
const eventBus = props.dashboard.events
|
|
|
|
? props.dashboard.events.newScopedBus(
|
|
|
|
`panel:${props.panel.id}`, // panelID
|
|
|
|
this.eventFilter
|
|
|
|
)
|
|
|
|
: new EventBusSrv();
|
|
|
|
|
2018-10-14 08:39:34 -05:00
|
|
|
this.state = {
|
2019-04-17 12:51:50 -05:00
|
|
|
isFirstLoad: true,
|
2018-11-05 10:46:09 -06:00
|
|
|
renderCounter: 0,
|
2019-05-03 08:35:37 -05:00
|
|
|
refreshWhenInView: false,
|
2021-04-30 15:33:29 -05:00
|
|
|
context: {
|
2021-05-10 07:24:23 -05:00
|
|
|
sync: props.isEditing ? DashboardCursorSync.Off : props.dashboard.graphTooltip,
|
2021-04-30 15:33:29 -05:00
|
|
|
eventBus,
|
2021-05-03 10:23:17 -05:00
|
|
|
onSeriesColorChange: this.onSeriesColorChange,
|
2021-05-06 14:22:03 -05:00
|
|
|
onToggleSeriesVisibility: this.onSeriesVisibilityChange,
|
2021-04-30 15:33:29 -05:00
|
|
|
},
|
2021-04-28 07:46:10 -05:00
|
|
|
data: this.getInitialPanelDataState(),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2021-05-03 10:23:17 -05:00
|
|
|
onSeriesColorChange = (label: string, color: string) => {
|
|
|
|
this.onFieldConfigChange(changeSeriesColorConfigFactory(label, color, this.props.panel.fieldConfig));
|
|
|
|
};
|
|
|
|
|
2021-05-06 14:22:03 -05:00
|
|
|
onSeriesVisibilityChange = (label: string, mode: SeriesVisibilityChangeMode) => {
|
|
|
|
this.onFieldConfigChange(
|
|
|
|
seriesVisibilityConfigFactory(label, mode, this.props.panel.fieldConfig, this.state.data.series)
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2021-04-28 07:46:10 -05:00
|
|
|
getInitialPanelDataState(): PanelData {
|
|
|
|
return {
|
|
|
|
state: LoadingState.NotStarted,
|
|
|
|
series: [],
|
|
|
|
timeRange: getDefaultTimeRange(),
|
2018-10-14 08:39:34 -05:00
|
|
|
};
|
2018-07-05 15:10:39 -05:00
|
|
|
}
|
|
|
|
|
2018-11-06 09:37:51 -06:00
|
|
|
componentDidMount() {
|
2020-05-25 07:05:43 -05:00
|
|
|
const { panel, dashboard } = this.props;
|
2020-04-10 09:37:26 -05:00
|
|
|
|
2020-11-03 06:08:54 -06:00
|
|
|
// Subscribe to panel events
|
2020-12-23 03:45:31 -06:00
|
|
|
this.subs.add(panel.events.subscribe(RefreshEvent, this.onRefresh));
|
|
|
|
this.subs.add(panel.events.subscribe(RenderEvent, this.onRender));
|
2020-05-01 07:14:52 -05:00
|
|
|
|
2019-04-17 12:51:50 -05:00
|
|
|
dashboard.panelInitialized(this.props.panel);
|
|
|
|
|
|
|
|
// Move snapshot data into the query response
|
|
|
|
if (this.hasPanelSnapshot) {
|
|
|
|
this.setState({
|
2020-11-17 03:25:36 -06:00
|
|
|
data: loadSnapshotData(panel, dashboard),
|
2019-04-17 12:51:50 -05:00
|
|
|
isFirstLoad: false,
|
|
|
|
});
|
2020-05-25 07:05:43 -05:00
|
|
|
return;
|
2019-04-17 12:51:50 -05:00
|
|
|
}
|
2020-05-01 07:14:52 -05:00
|
|
|
|
2020-05-25 07:05:43 -05:00
|
|
|
if (!this.wantsQueryExecution) {
|
|
|
|
this.setState({ isFirstLoad: false });
|
2020-05-01 07:14:52 -05:00
|
|
|
}
|
2020-05-25 07:05:43 -05:00
|
|
|
|
2020-12-23 03:45:31 -06:00
|
|
|
this.subs.add(
|
|
|
|
panel
|
|
|
|
.getQueryRunner()
|
|
|
|
.getData({ withTransforms: true, withFieldConfig: true })
|
|
|
|
.subscribe({
|
2021-01-20 00:59:48 -06:00
|
|
|
next: (data) => this.onDataUpdate(data),
|
2020-12-23 03:45:31 -06:00
|
|
|
})
|
|
|
|
);
|
2018-10-14 08:39:34 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
2020-12-23 03:45:31 -06:00
|
|
|
this.subs.unsubscribe();
|
2018-10-14 08:39:34 -05:00
|
|
|
}
|
|
|
|
|
2019-05-03 08:35:37 -05:00
|
|
|
componentDidUpdate(prevProps: Props) {
|
2021-05-10 07:24:23 -05:00
|
|
|
const { isInView, isEditing } = this.props;
|
|
|
|
|
|
|
|
if (prevProps.dashboard.graphTooltip !== this.props.dashboard.graphTooltip) {
|
|
|
|
this.setState((s) => {
|
|
|
|
return {
|
|
|
|
context: { ...s.context, sync: isEditing ? DashboardCursorSync.Off : this.props.dashboard.graphTooltip },
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isEditing !== prevProps.isEditing) {
|
|
|
|
this.setState((s) => {
|
|
|
|
return {
|
|
|
|
context: { ...s.context, sync: isEditing ? DashboardCursorSync.Off : this.props.dashboard.graphTooltip },
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
2019-05-03 08:35:37 -05:00
|
|
|
|
|
|
|
// View state has changed
|
|
|
|
if (isInView !== prevProps.isInView) {
|
|
|
|
if (isInView) {
|
|
|
|
// Check if we need a delayed refresh
|
|
|
|
if (this.state.refreshWhenInView) {
|
|
|
|
this.onRefresh();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-12-17 04:45:28 -06:00
|
|
|
shouldComponentUpdate(prevProps: Props, prevState: State) {
|
|
|
|
const { plugin, panel } = this.props;
|
|
|
|
|
|
|
|
// If plugin changed we need to process fieldOverrides again
|
|
|
|
// We do this by asking panel query runner to resend last result
|
|
|
|
if (prevProps.plugin !== plugin) {
|
|
|
|
panel.getQueryRunner().resendLastResult();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2019-04-17 12:51:50 -05:00
|
|
|
// Updates the response with information from the stream
|
2019-04-18 01:22:14 -05:00
|
|
|
// The next is outside a react synthetic event so setState is not batched
|
|
|
|
// So in this context we can only do a single call to setState
|
2019-12-19 00:01:35 -06:00
|
|
|
onDataUpdate(data: PanelData) {
|
2021-04-28 07:46:10 -05:00
|
|
|
const { isInView, dashboard, panel, plugin } = this.props;
|
|
|
|
|
|
|
|
if (!isInView) {
|
2021-02-24 04:12:11 -06:00
|
|
|
if (data.state !== LoadingState.Streaming) {
|
|
|
|
// Ignore events when not visible.
|
|
|
|
// The call will be repeated when the panel comes into view
|
|
|
|
this.setState({ refreshWhenInView: true });
|
|
|
|
}
|
2021-04-28 07:46:10 -05:00
|
|
|
return;
|
|
|
|
}
|
2021-02-24 04:12:11 -06:00
|
|
|
|
2021-04-28 07:46:10 -05:00
|
|
|
// Ignore this data update if we are now a non data panel
|
|
|
|
if (plugin.meta.skipDataQuery) {
|
|
|
|
this.setState({ data: this.getInitialPanelDataState() });
|
2019-12-19 00:01:35 -06:00
|
|
|
return;
|
|
|
|
}
|
2019-05-03 08:35:37 -05:00
|
|
|
|
2019-12-19 00:01:35 -06:00
|
|
|
let { isFirstLoad } = this.state;
|
2020-02-17 00:25:27 -06:00
|
|
|
let errorMessage: string | undefined;
|
2019-10-01 03:22:41 -05:00
|
|
|
|
2019-12-19 00:01:35 -06:00
|
|
|
switch (data.state) {
|
|
|
|
case LoadingState.Loading:
|
|
|
|
// Skip updating state data if it is already in loading state
|
|
|
|
// This is to avoid rendering partial loading responses
|
|
|
|
if (this.state.data.state === LoadingState.Loading) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LoadingState.Error:
|
|
|
|
const { error } = data;
|
|
|
|
if (error) {
|
|
|
|
if (errorMessage !== error.message) {
|
|
|
|
errorMessage = error.message;
|
2019-10-01 03:22:41 -05:00
|
|
|
}
|
2019-12-19 00:01:35 -06:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case LoadingState.Done:
|
|
|
|
// If we are doing a snapshot save data in panel model
|
2021-04-28 07:46:10 -05:00
|
|
|
if (dashboard.snapshot) {
|
|
|
|
panel.snapshotData = data.series.map((frame) => toDataFrameDTO(frame));
|
2019-12-19 00:01:35 -06:00
|
|
|
}
|
|
|
|
if (isFirstLoad) {
|
|
|
|
isFirstLoad = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
2019-04-18 01:22:14 -05:00
|
|
|
|
2019-12-19 00:01:35 -06:00
|
|
|
this.setState({ isFirstLoad, errorMessage, data });
|
|
|
|
}
|
2019-04-17 12:51:50 -05:00
|
|
|
|
2018-10-14 08:39:34 -05:00
|
|
|
onRefresh = () => {
|
2019-05-03 08:35:37 -05:00
|
|
|
const { panel, isInView, width } = this.props;
|
|
|
|
if (!isInView) {
|
|
|
|
this.setState({ refreshWhenInView: true });
|
2018-11-08 07:44:12 -06:00
|
|
|
return;
|
|
|
|
}
|
2018-11-12 04:23:41 -06:00
|
|
|
|
2018-11-13 08:05:07 -06:00
|
|
|
const timeData = applyPanelTimeOverrides(panel, this.timeSrv.timeRange());
|
2018-07-09 15:24:15 -05:00
|
|
|
|
2019-04-17 12:51:50 -05:00
|
|
|
// Issue Query
|
2019-04-18 01:22:14 -05:00
|
|
|
if (this.wantsQueryExecution) {
|
2019-04-17 12:51:50 -05:00
|
|
|
if (width < 0) {
|
|
|
|
return;
|
|
|
|
}
|
2019-04-18 16:10:18 -05:00
|
|
|
|
2020-05-01 07:14:52 -05:00
|
|
|
panel.getQueryRunner().run({
|
2019-04-17 12:51:50 -05:00
|
|
|
datasource: panel.datasource,
|
|
|
|
queries: panel.targets,
|
2021-05-03 01:52:05 -05:00
|
|
|
panelId: panel.editSourceId || panel.id,
|
2019-04-17 12:51:50 -05:00
|
|
|
dashboardId: this.props.dashboard.id,
|
2019-08-13 00:32:43 -05:00
|
|
|
timezone: this.props.dashboard.getTimezone(),
|
2019-04-17 12:51:50 -05:00
|
|
|
timeRange: timeData.timeRange,
|
2019-04-18 02:00:46 -05:00
|
|
|
timeInfo: timeData.timeInfo,
|
2020-04-27 11:29:41 -05:00
|
|
|
maxDataPoints: panel.maxDataPoints || width,
|
2019-04-18 16:10:18 -05:00
|
|
|
minInterval: panel.interval,
|
2019-04-17 12:51:50 -05:00
|
|
|
scopedVars: panel.scopedVars,
|
|
|
|
cacheTimeout: panel.cacheTimeout,
|
2019-09-09 01:58:57 -05:00
|
|
|
transformations: panel.transformations,
|
2019-04-17 12:51:50 -05:00
|
|
|
});
|
2020-05-21 02:55:58 -05:00
|
|
|
} else {
|
|
|
|
// The panel should render on refresh as well if it doesn't have a query, like clock panel
|
2021-04-13 10:55:55 -05:00
|
|
|
this.setState((prevState) => ({
|
|
|
|
data: { ...prevState.data, timeRange: this.timeSrv.timeRange() },
|
|
|
|
}));
|
2019-04-17 12:51:50 -05:00
|
|
|
}
|
2018-10-14 08:39:34 -05:00
|
|
|
};
|
2018-07-09 15:24:15 -05:00
|
|
|
|
2018-11-05 10:46:09 -06:00
|
|
|
onRender = () => {
|
2019-04-25 13:01:02 -05:00
|
|
|
const stateUpdate = { renderCounter: this.state.renderCounter + 1 };
|
|
|
|
this.setState(stateUpdate);
|
2018-11-03 17:36:40 -05:00
|
|
|
};
|
|
|
|
|
2019-04-24 03:14:18 -05:00
|
|
|
onOptionsChange = (options: any) => {
|
|
|
|
this.props.panel.updateOptions(options);
|
|
|
|
};
|
|
|
|
|
2020-03-19 05:50:31 -05:00
|
|
|
onFieldConfigChange = (config: FieldConfigSource) => {
|
|
|
|
this.props.panel.updateFieldConfig(config);
|
|
|
|
};
|
|
|
|
|
2019-02-14 08:18:55 -06:00
|
|
|
onPanelError = (message: string) => {
|
|
|
|
if (this.state.errorMessage !== message) {
|
|
|
|
this.setState({ errorMessage: message });
|
|
|
|
}
|
2019-01-30 03:39:42 -06:00
|
|
|
};
|
|
|
|
|
2019-02-12 05:25:10 -06:00
|
|
|
get hasPanelSnapshot() {
|
|
|
|
const { panel } = this.props;
|
|
|
|
return panel.snapshotData && panel.snapshotData.length;
|
|
|
|
}
|
|
|
|
|
2019-04-17 12:51:50 -05:00
|
|
|
get wantsQueryExecution() {
|
2020-02-08 06:23:16 -06:00
|
|
|
return !(this.props.plugin.meta.skipDataQuery || this.hasPanelSnapshot);
|
2019-02-12 05:25:10 -06:00
|
|
|
}
|
|
|
|
|
2019-09-16 02:31:22 -05:00
|
|
|
onChangeTimeRange = (timeRange: AbsoluteTimeRange) => {
|
|
|
|
this.timeSrv.setTime({
|
|
|
|
from: toUtc(timeRange.from),
|
|
|
|
to: toUtc(timeRange.to),
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
2020-08-20 01:52:26 -05:00
|
|
|
shouldSignalRenderingCompleted(loadingState: LoadingState, pluginMeta: PanelPluginMeta) {
|
|
|
|
return loadingState === LoadingState.Done || pluginMeta.skipDataQuery;
|
|
|
|
}
|
|
|
|
|
2020-02-17 00:25:27 -06:00
|
|
|
renderPanel(width: number, height: number) {
|
2020-11-03 06:08:54 -06:00
|
|
|
const { panel, plugin, dashboard } = this.props;
|
2019-04-18 02:00:46 -05:00
|
|
|
const { renderCounter, data, isFirstLoad } = this.state;
|
2019-05-06 08:26:09 -05:00
|
|
|
const { theme } = config;
|
2020-08-20 01:52:26 -05:00
|
|
|
const { state: loadingState } = data;
|
|
|
|
|
|
|
|
// do not render component until we have first data
|
|
|
|
if (isFirstLoad && (loadingState === LoadingState.Loading || loadingState === LoadingState.NotStarted)) {
|
|
|
|
return null;
|
|
|
|
}
|
2019-01-30 03:39:42 -06:00
|
|
|
|
2019-02-01 01:12:58 -06:00
|
|
|
// This is only done to increase a counter that is used by backend
|
2020-04-15 15:17:41 -05:00
|
|
|
// image rendering to know when to capture image
|
2020-08-20 01:52:26 -05:00
|
|
|
if (this.shouldSignalRenderingCompleted(loadingState, plugin.meta)) {
|
2019-05-13 02:38:19 -05:00
|
|
|
profiler.renderingCompleted();
|
2019-02-01 01:12:58 -06:00
|
|
|
}
|
|
|
|
|
2020-07-09 08:16:35 -05:00
|
|
|
const PanelComponent = plugin.panel!;
|
2019-09-25 04:19:17 -05:00
|
|
|
const timeRange = data.timeRange || this.timeSrv.timeRange();
|
2019-11-25 15:26:18 -06:00
|
|
|
const headerHeight = this.hasOverlayHeader() ? 0 : theme.panelHeaderHeight;
|
|
|
|
const chromePadding = plugin.noPadding ? 0 : theme.panelPadding;
|
|
|
|
const panelWidth = width - chromePadding * 2 - PANEL_BORDER;
|
|
|
|
const innerPanelHeight = height - headerHeight - chromePadding * 2 - PANEL_BORDER;
|
|
|
|
const panelContentClassNames = classNames({
|
|
|
|
'panel-content': true,
|
|
|
|
'panel-content--no-padding': plugin.noPadding,
|
|
|
|
});
|
2020-03-16 08:26:03 -05:00
|
|
|
const panelOptions = panel.getOptions();
|
2019-11-25 15:26:18 -06:00
|
|
|
|
2021-04-30 15:33:29 -05:00
|
|
|
// Update the event filter (dashboard settings may have changed)
|
|
|
|
// Yes this is called ever render for a function that is triggered on every mouse move
|
|
|
|
this.eventFilter.onlyLocal = dashboard.graphTooltip === 0;
|
|
|
|
|
2019-02-07 14:34:50 -06:00
|
|
|
return (
|
|
|
|
<>
|
2019-11-25 15:26:18 -06:00
|
|
|
<div className={panelContentClassNames}>
|
2021-04-30 15:33:29 -05:00
|
|
|
<PanelContextProvider value={this.state.context}>
|
2021-04-26 09:13:15 -05:00
|
|
|
<PanelComponent
|
|
|
|
id={panel.id}
|
|
|
|
data={data}
|
|
|
|
title={panel.title}
|
|
|
|
timeRange={timeRange}
|
|
|
|
timeZone={this.props.dashboard.getTimezone()}
|
|
|
|
options={panelOptions}
|
|
|
|
fieldConfig={panel.fieldConfig}
|
|
|
|
transparent={panel.transparent}
|
|
|
|
width={panelWidth}
|
|
|
|
height={innerPanelHeight}
|
|
|
|
renderCounter={renderCounter}
|
|
|
|
replaceVariables={panel.replaceVariables}
|
|
|
|
onOptionsChange={this.onOptionsChange}
|
|
|
|
onFieldConfigChange={this.onFieldConfigChange}
|
|
|
|
onChangeTimeRange={this.onChangeTimeRange}
|
|
|
|
eventBus={dashboard.events}
|
|
|
|
/>
|
|
|
|
</PanelContextProvider>
|
2019-04-17 12:51:50 -05:00
|
|
|
</div>
|
2019-02-07 14:34:50 -06:00
|
|
|
</>
|
|
|
|
);
|
2019-04-17 12:51:50 -05:00
|
|
|
}
|
|
|
|
|
2019-11-25 15:26:18 -06:00
|
|
|
hasOverlayHeader() {
|
|
|
|
const { panel } = this.props;
|
|
|
|
const { errorMessage, data } = this.state;
|
|
|
|
|
|
|
|
// always show normal header if we have an error message
|
|
|
|
if (errorMessage) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// always show normal header if we have time override
|
|
|
|
if (data.request && data.request.timeInfo) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return !panel.hasTitle();
|
|
|
|
}
|
|
|
|
|
2018-10-14 08:39:34 -05:00
|
|
|
render() {
|
2021-02-08 23:05:34 -06:00
|
|
|
const { dashboard, panel, isViewing, isEditing, width, height } = this.props;
|
2019-04-18 02:00:46 -05:00
|
|
|
const { errorMessage, data } = this.state;
|
2019-02-07 14:34:50 -06:00
|
|
|
const { transparent } = panel;
|
2018-11-06 08:03:56 -06:00
|
|
|
|
2021-05-03 01:52:05 -05:00
|
|
|
let alertState = data.alertState?.state;
|
|
|
|
|
2019-05-06 08:26:09 -05:00
|
|
|
const containerClassNames = classNames({
|
|
|
|
'panel-container': true,
|
|
|
|
'panel-container--absolute': true,
|
2019-11-25 15:26:18 -06:00
|
|
|
'panel-container--transparent': transparent,
|
|
|
|
'panel-container--no-title': this.hasOverlayHeader(),
|
2021-05-03 01:52:05 -05:00
|
|
|
[`panel-alert-state--${alertState}`]: alertState !== undefined,
|
2019-05-06 08:26:09 -05:00
|
|
|
});
|
|
|
|
|
2018-06-19 07:51:57 -05:00
|
|
|
return (
|
2020-05-11 04:46:57 -05:00
|
|
|
<div className={containerClassNames} aria-label={selectors.components.Panels.Panel.containerByTitle(panel.title)}>
|
2019-04-17 12:51:50 -05:00
|
|
|
<PanelHeader
|
|
|
|
panel={panel}
|
|
|
|
dashboard={dashboard}
|
|
|
|
title={panel.title}
|
|
|
|
description={panel.description}
|
|
|
|
links={panel.links}
|
|
|
|
error={errorMessage}
|
2020-04-10 09:37:26 -05:00
|
|
|
isEditing={isEditing}
|
|
|
|
isViewing={isViewing}
|
2021-05-03 01:52:05 -05:00
|
|
|
alertState={alertState}
|
2020-03-18 07:00:14 -05:00
|
|
|
data={data}
|
2019-04-17 12:51:50 -05:00
|
|
|
/>
|
|
|
|
<ErrorBoundary>
|
2020-02-17 00:25:27 -06:00
|
|
|
{({ error }) => {
|
|
|
|
if (error) {
|
2019-04-17 12:51:50 -05:00
|
|
|
this.onPanelError(error.message || DEFAULT_PLUGIN_ERROR);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return this.renderPanel(width, height);
|
|
|
|
}}
|
|
|
|
</ErrorBoundary>
|
|
|
|
</div>
|
2018-06-19 07:51:57 -05:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|