mirror of
https://github.com/grafana/grafana.git
synced 2026-08-11 13:44:59 -05:00
Datatrails: hide empty breakdown panels, improve loading status (#85028)
feat: hide empty breakdown panels, improve loading status
This commit is contained in:
@@ -4,7 +4,6 @@ import React from 'react';
|
||||
|
||||
import { DataFrame, FieldType, GrafanaTheme2, PanelData, SelectableValue } from '@grafana/data';
|
||||
import {
|
||||
FieldConfigBuilders,
|
||||
PanelBuilders,
|
||||
QueryVariable,
|
||||
SceneComponentProps,
|
||||
@@ -299,7 +298,6 @@ export function buildAllLayout(options: Array<SelectableValue<string>>, queryDef
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
return new LayoutSwitcher({
|
||||
options: [
|
||||
{ value: 'grid', label: 'Grid' },
|
||||
@@ -328,6 +326,32 @@ const GRID_TEMPLATE_COLUMNS = 'repeat(auto-fit, minmax(400px, 1fr))';
|
||||
function buildNormalLayout(queryDef: AutoQueryDef) {
|
||||
const unit = queryDef.unit;
|
||||
|
||||
function getLayoutChild(data: PanelData, frame: DataFrame, frameIndex: number): SceneFlexItem {
|
||||
const vizPanel: VizPanel = queryDef
|
||||
.vizBuilder()
|
||||
.setTitle(getLabelValue(frame))
|
||||
.setData(new SceneDataNode({ data: { ...data, series: [frame] } }))
|
||||
.setColor({ mode: 'fixed', fixedColor: getColorByIndex(frameIndex) })
|
||||
.setHeaderActions(new AddToFiltersGraphAction({ frame }))
|
||||
.setUnit(unit)
|
||||
.build();
|
||||
|
||||
// Find a frame that has at more than one point.
|
||||
const isHidden = frame.length <= 1;
|
||||
|
||||
const item: SceneCSSGridItem = new SceneCSSGridItem({
|
||||
$behaviors: [yAxisSyncBehavior],
|
||||
body: vizPanel,
|
||||
isHidden,
|
||||
});
|
||||
|
||||
vizPanel.addActivationHandler(() => {
|
||||
vizPanel.onOptionsChange(breakdownPanelOptions);
|
||||
});
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
return new LayoutSwitcher({
|
||||
$data: new SceneQueryRunner({
|
||||
datasource: trailDS,
|
||||
@@ -355,29 +379,7 @@ function buildNormalLayout(queryDef: AutoQueryDef) {
|
||||
autoRows: '200px',
|
||||
children: [],
|
||||
}),
|
||||
getLayoutChild: (data, frame, frameIndex) => {
|
||||
const vizPanel = queryDef
|
||||
.vizBuilder()
|
||||
.setTitle(getLabelValue(frame))
|
||||
.setData(new SceneDataNode({ data: { ...data, series: [frame] } }))
|
||||
.setColor({ mode: 'fixed', fixedColor: getColorByIndex(frameIndex) })
|
||||
.setHeaderActions(new AddToFiltersGraphAction({ frame }))
|
||||
.setUnit(unit)
|
||||
.build();
|
||||
|
||||
if (vizPanel.isActive) {
|
||||
vizPanel.onOptionsChange(breakdownPanelOptions);
|
||||
} else {
|
||||
vizPanel.addActivationHandler(() => {
|
||||
vizPanel.onOptionsChange(breakdownPanelOptions);
|
||||
});
|
||||
}
|
||||
|
||||
return new SceneCSSGridItem({
|
||||
$behaviors: [yAxisSyncBehavior],
|
||||
body: vizPanel,
|
||||
});
|
||||
},
|
||||
getLayoutChild,
|
||||
}),
|
||||
new ByFrameRepeater({
|
||||
body: new SceneCSSGridLayout({
|
||||
@@ -385,31 +387,7 @@ function buildNormalLayout(queryDef: AutoQueryDef) {
|
||||
autoRows: '200px',
|
||||
children: [],
|
||||
}),
|
||||
getLayoutChild: (data, frame, frameIndex) => {
|
||||
const vizPanel: VizPanel = queryDef
|
||||
.vizBuilder()
|
||||
.setTitle(getLabelValue(frame))
|
||||
.setData(new SceneDataNode({ data: { ...data, series: [frame] } }))
|
||||
.setColor({ mode: 'fixed', fixedColor: getColorByIndex(frameIndex) })
|
||||
.setHeaderActions(new AddToFiltersGraphAction({ frame }))
|
||||
.setUnit(unit)
|
||||
.build();
|
||||
|
||||
if (vizPanel.isActive) {
|
||||
vizPanel.onOptionsChange(breakdownPanelOptions);
|
||||
} else {
|
||||
vizPanel.addActivationHandler(() => {
|
||||
vizPanel.onOptionsChange(breakdownPanelOptions);
|
||||
});
|
||||
}
|
||||
|
||||
FieldConfigBuilders.timeseries().build();
|
||||
|
||||
return new SceneCSSGridItem({
|
||||
$behaviors: [yAxisSyncBehavior],
|
||||
body: vizPanel,
|
||||
});
|
||||
},
|
||||
getLayoutChild,
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
@@ -7,10 +7,14 @@ import {
|
||||
SceneObjectBase,
|
||||
sceneGraph,
|
||||
SceneComponentProps,
|
||||
SceneByFrameRepeater,
|
||||
SceneLayout,
|
||||
SceneDataNode,
|
||||
} from '@grafana/scenes';
|
||||
|
||||
import { StatusWrapper } from '../StatusWrapper';
|
||||
|
||||
import { findSceneObjectsByType } from './utils';
|
||||
|
||||
interface ByFrameRepeaterState extends SceneObjectState {
|
||||
body: SceneLayout;
|
||||
getLayoutChild(data: PanelData, frame: DataFrame, frameIndex: number): SceneFlexItem;
|
||||
@@ -24,9 +28,20 @@ export class ByFrameRepeater extends SceneObjectBase<ByFrameRepeaterState> {
|
||||
const data = sceneGraph.getData(this);
|
||||
|
||||
this._subs.add(
|
||||
data.subscribeToState((data) => {
|
||||
if (data.data?.state === LoadingState.Done) {
|
||||
this.performRepeat(data.data);
|
||||
data.subscribeToState((newState, oldState) => {
|
||||
if (newState.data === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newData = newState.data;
|
||||
|
||||
if (newState.data !== undefined && newState.data?.state !== oldState.data?.state) {
|
||||
findSceneObjectsByType(this, SceneDataNode).forEach((dataNode) => {
|
||||
dataNode.setState({ data: { ...dataNode.state.data, state: newData.state } });
|
||||
});
|
||||
}
|
||||
if (newData.state === LoadingState.Done) {
|
||||
this.performRepeat(newData);
|
||||
}
|
||||
})
|
||||
);
|
||||
@@ -41,15 +56,39 @@ export class ByFrameRepeater extends SceneObjectBase<ByFrameRepeaterState> {
|
||||
const newChildren: SceneFlexItem[] = [];
|
||||
|
||||
for (let seriesIndex = 0; seriesIndex < data.series.length; seriesIndex++) {
|
||||
const layoutChild = this.state.getLayoutChild(data, data.series[seriesIndex], seriesIndex);
|
||||
const frame = data.series[seriesIndex];
|
||||
if (frame.length <= 1) {
|
||||
// If the data doesn't have at least two points, we skip it.
|
||||
continue;
|
||||
}
|
||||
const layoutChild = this.state.getLayoutChild(data, frame, seriesIndex);
|
||||
newChildren.push(layoutChild);
|
||||
}
|
||||
|
||||
this.state.body.setState({ children: newChildren });
|
||||
this.setState({ body: this.state.body });
|
||||
}
|
||||
|
||||
public static Component = ({ model }: SceneComponentProps<SceneByFrameRepeater>) => {
|
||||
public static Component = ({ model }: SceneComponentProps<ByFrameRepeater>) => {
|
||||
const { body } = model.useState();
|
||||
return <body.Component model={body} />;
|
||||
const { children } = body.useState();
|
||||
|
||||
const data = sceneGraph.getData(model);
|
||||
const sceneDataState = data.useState();
|
||||
|
||||
const panelData = sceneDataState?.data;
|
||||
|
||||
const isLoading = panelData?.state === 'Loading' && children.length === 0;
|
||||
const error = panelData?.state === LoadingState.Error ? 'Failed to load data.' : undefined;
|
||||
const blockingMessage =
|
||||
!isLoading && children.length === 0 && !error
|
||||
? 'There is no data available. Try adjusting your filters, time range, or selecting a different label.'
|
||||
: undefined;
|
||||
|
||||
return (
|
||||
<StatusWrapper {...{ blockingMessage, error, isLoading }}>
|
||||
<body.Component model={body} />
|
||||
</StatusWrapper>
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ function getStyles(theme: GrafanaTheme2) {
|
||||
fontStyle: 'italic',
|
||||
marginTop: theme.spacing(7),
|
||||
textAlign: 'center',
|
||||
width: '100%',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user