mirror of
https://github.com/grafana/grafana.git
synced 2026-09-05 04:40:13 -05:00
TimeSeries: Explicitly add transformer when timeseries-long exists (#64092)
This commit is contained in:
@@ -9,6 +9,14 @@ import { configureStore } from 'app/store/configureStore';
|
||||
|
||||
import { PanelDataErrorView } from './PanelDataErrorView';
|
||||
|
||||
jest.mock('app/features/dashboard/services/DashboardSrv', () => ({
|
||||
getDashboardSrv: () => {
|
||||
return {
|
||||
getCurrent: () => undefined,
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
describe('PanelDataErrorView', () => {
|
||||
it('show No data when there is no data', () => {
|
||||
renderWithProps();
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
import { css } from '@emotion/css';
|
||||
import React from 'react';
|
||||
|
||||
import { CoreApp, GrafanaTheme2, PanelDataSummary, VisualizationSuggestionsBuilder } from '@grafana/data';
|
||||
import { PanelDataErrorViewProps } from '@grafana/runtime';
|
||||
import {
|
||||
CoreApp,
|
||||
GrafanaTheme2,
|
||||
PanelDataSummary,
|
||||
VisualizationSuggestionsBuilder,
|
||||
VisualizationSuggestion,
|
||||
} from '@grafana/data';
|
||||
import { PanelDataErrorViewProps, locationService } from '@grafana/runtime';
|
||||
import { usePanelContext, useStyles2 } from '@grafana/ui';
|
||||
import { CardButton } from 'app/core/components/CardButton';
|
||||
import { LS_VISUALIZATION_SELECT_TAB_KEY } from 'app/core/constants';
|
||||
@@ -21,6 +27,7 @@ export function PanelDataErrorView(props: PanelDataErrorViewProps) {
|
||||
const { dataSummary } = builder;
|
||||
const message = getMessageFor(props, dataSummary);
|
||||
const dispatch = useDispatch();
|
||||
const panel = getDashboardSrv().getCurrent()?.getPanelById(props.panelId);
|
||||
|
||||
const openVizPicker = () => {
|
||||
store.setObject(LS_VISUALIZATION_SELECT_TAB_KEY, VisualizationSelectPaneTab.Suggestions);
|
||||
@@ -28,7 +35,6 @@ export function PanelDataErrorView(props: PanelDataErrorViewProps) {
|
||||
};
|
||||
|
||||
const switchToTable = () => {
|
||||
const panel = getDashboardSrv().getCurrent()?.getPanelById(props.panelId);
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
@@ -41,11 +47,37 @@ export function PanelDataErrorView(props: PanelDataErrorViewProps) {
|
||||
);
|
||||
};
|
||||
|
||||
const loadSuggestion = (s: VisualizationSuggestion) => {
|
||||
if (!panel) {
|
||||
return;
|
||||
}
|
||||
dispatch(
|
||||
changePanelPlugin({
|
||||
...s, // includes panelId, config, etc
|
||||
panel,
|
||||
})
|
||||
);
|
||||
if (s.transformations) {
|
||||
setTimeout(() => {
|
||||
locationService.partial({ tab: 'transform' });
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={styles.wrapper}>
|
||||
<div className={styles.message}>{message}</div>
|
||||
{context.app === CoreApp.PanelEditor && dataSummary.hasData && (
|
||||
{context.app === CoreApp.PanelEditor && dataSummary.hasData && panel && (
|
||||
<div className={styles.actions}>
|
||||
{props.suggestions && (
|
||||
<>
|
||||
{props.suggestions.map((v) => (
|
||||
<CardButton key={v.name} icon="process" onClick={() => loadSuggestion(v)}>
|
||||
{v.name}
|
||||
</CardButton>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
<CardButton icon="table" onClick={switchToTable}>
|
||||
Switch to table
|
||||
</CardButton>
|
||||
|
||||
@@ -21,6 +21,14 @@ jest.mock('app/features/plugins/importPanelPlugin', () => {
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('app/features/dashboard/services/DashboardSrv', () => ({
|
||||
getDashboardSrv: () => {
|
||||
return {
|
||||
getCurrent: () => undefined,
|
||||
};
|
||||
},
|
||||
}));
|
||||
|
||||
standardFieldConfigEditorRegistry.setInit(() => mockStandardFieldConfigOptions());
|
||||
standardEditorsRegistry.setInit(() => mockStandardFieldConfigOptions());
|
||||
|
||||
|
||||
@@ -56,10 +56,11 @@ export function changePanelPlugin({
|
||||
pluginId,
|
||||
options,
|
||||
fieldConfig,
|
||||
transformations,
|
||||
}: ChangePanelPluginAndOptionsArgs): ThunkResult<void> {
|
||||
return async (dispatch, getStore) => {
|
||||
// ignore action is no change
|
||||
if (panel.type === pluginId && !options && !fieldConfig) {
|
||||
if (panel.type === pluginId && !options && !fieldConfig && !transformations) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -74,7 +75,7 @@ export function changePanelPlugin({
|
||||
panel.changePlugin(plugin);
|
||||
}
|
||||
|
||||
if (options || fieldConfig) {
|
||||
if (options || fieldConfig || transformations) {
|
||||
const newOptions = getPanelOptionsWithDefaults({
|
||||
plugin,
|
||||
currentOptions: options || panel.options,
|
||||
@@ -84,6 +85,7 @@ export function changePanelPlugin({
|
||||
|
||||
panel.options = newOptions.options;
|
||||
panel.fieldConfig = newOptions.fieldConfig;
|
||||
panel.transformations = transformations || panel.transformations;
|
||||
panel.configRev++;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { Field, PanelProps } from '@grafana/data';
|
||||
import { Field, PanelProps, DataFrameType } from '@grafana/data';
|
||||
import { PanelDataErrorView } from '@grafana/runtime';
|
||||
import { TooltipDisplayMode } from '@grafana/schema';
|
||||
import { KeyboardPlugin, TimeSeries, TooltipPlugin, usePanelContext, ZoomPlugin } from '@grafana/ui';
|
||||
@@ -14,6 +14,7 @@ import { ContextMenuPlugin } from './plugins/ContextMenuPlugin';
|
||||
import { ExemplarsPlugin, getVisibleLabels } from './plugins/ExemplarsPlugin';
|
||||
import { OutsideRangePlugin } from './plugins/OutsideRangePlugin';
|
||||
import { ThresholdControlsPlugin } from './plugins/ThresholdControlsPlugin';
|
||||
import { getPrepareTimeseriesSuggestion } from './suggestions';
|
||||
import { getTimezones, prepareGraphableFields, regenerateLinksSupplier } from './utils';
|
||||
|
||||
interface TimeSeriesPanelProps extends PanelProps<PanelOptions> {}
|
||||
@@ -39,15 +40,27 @@ export const TimeSeriesPanel = ({
|
||||
|
||||
const frames = useMemo(() => prepareGraphableFields(data.series, config.theme2, timeRange), [data, timeRange]);
|
||||
const timezones = useMemo(() => getTimezones(options.timezone, timeZone), [options.timezone, timeZone]);
|
||||
const suggestions = useMemo(() => {
|
||||
if (data.series.every((df) => df.meta?.type === DataFrameType.TimeSeriesLong)) {
|
||||
const s = getPrepareTimeseriesSuggestion(id);
|
||||
return {
|
||||
message: 'Long data must be converted to wide',
|
||||
suggestions: s ? [s] : undefined,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}, [data.series, id]);
|
||||
|
||||
if (!frames) {
|
||||
if (!frames || suggestions) {
|
||||
return (
|
||||
<PanelDataErrorView
|
||||
panelId={id}
|
||||
message={suggestions?.message}
|
||||
fieldConfig={fieldConfig}
|
||||
data={data}
|
||||
needsTimeField={true}
|
||||
needsNumberField={true}
|
||||
suggestions={suggestions?.suggestions}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import { FieldColorModeId, VisualizationSuggestionsBuilder } from '@grafana/data';
|
||||
import {
|
||||
FieldColorModeId,
|
||||
VisualizationSuggestionsBuilder,
|
||||
VisualizationSuggestion,
|
||||
DataTransformerID,
|
||||
} from '@grafana/data';
|
||||
import { GraphDrawStyle, GraphFieldConfig, GraphGradientMode, LineInterpolation, StackingMode } from '@grafana/schema';
|
||||
import { getDashboardSrv } from 'app/features/dashboard/services/DashboardSrv';
|
||||
import { SuggestionName } from 'app/types/suggestions';
|
||||
|
||||
import { PanelOptions } from './panelcfg.gen';
|
||||
@@ -200,3 +206,24 @@ export class TimeSeriesSuggestionsSupplier {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This will try to get a suggestion that will add a long to wide conversion
|
||||
export function getPrepareTimeseriesSuggestion(panelId: number): VisualizationSuggestion | undefined {
|
||||
const panel = getDashboardSrv().getCurrent()?.getPanelById(panelId);
|
||||
if (panel) {
|
||||
const transformations = panel.transformations ? [...panel.transformations] : [];
|
||||
transformations.push({
|
||||
id: DataTransformerID.prepareTimeSeries,
|
||||
options: {
|
||||
format: 'wide',
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
name: 'Transform to wide time series format',
|
||||
pluginId: 'timeseries',
|
||||
transformations,
|
||||
};
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user