TimeSeries: Explicitly add transformer when timeseries-long exists (#64092)

This commit is contained in:
Ryan McKinley
2023-04-28 06:10:02 +03:00
committed by GitHub
parent 21f6414f13
commit f5d97c677b
9 changed files with 959 additions and 11 deletions
@@ -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());
+4 -2
View File
@@ -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;
}