TextPanel: Fix suggestions for existing panels (#42195)

Closes #42118

Co-authored-by: kay delaney <kay@grafana.com>
Co-authored-by: Josh Hunt <josh@trtr.co>
Co-authored-by: Ashley Harrison <ashley.harrison@grafana.com>
Co-authored-by: Hugo Häggmark <hugo.haggmark@gmail.com>
This commit is contained in:
Hugo Häggmark 2021-11-24 11:20:36 +01:00 committed by GitHub
parent db122e9b2c
commit df22828d7d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 204 additions and 11 deletions

View File

@ -2,7 +2,7 @@ import React, { useMemo, useState } from 'react';
import { GrafanaTheme2, SelectableValue } from '@grafana/data';
import { CustomScrollbar, FilterInput, RadioButtonGroup, useStyles2 } from '@grafana/ui';
import { getPanelFrameCategory } from './getPanelFrameOptions';
import { getVizualizationOptions } from './getVizualizationOptions';
import { getVisualizationOptions } from './getVisualizationOptions';
import { css } from '@emotion/css';
import { OptionsPaneCategory } from './OptionsPaneCategory';
import { getFieldOverrideCategories } from './getFieldOverrideElements';
@ -21,7 +21,7 @@ export const OptionsPaneOptions: React.FC<OptionPaneRenderProps> = (props) => {
const styles = useStyles2(getStyles);
const [panelFrameOptions, vizOptions, libraryPanelOptions] = useMemo(
() => [getPanelFrameCategory(props), getVizualizationOptions(props), getLibraryPanelOptionsCategory(props)],
() => [getPanelFrameCategory(props), getVisualizationOptions(props), getLibraryPanelOptionsCategory(props)],
// eslint-disable-next-line react-hooks/exhaustive-deps
[panel.configRev, props.data, props.instanceState, searchQuery]

View File

@ -0,0 +1,161 @@
import { EventBusSrv, FieldType, getDefaultTimeRange, LoadingState, toDataFrame } from '@grafana/data';
import { getStandardEditorContext } from './getVisualizationOptions';
describe('getStandardEditorContext', () => {
it('defaults the series data to an empty array', () => {
const editorContext = getStandardEditorContext({
data: undefined,
replaceVariables: jest.fn(),
options: {},
eventBus: new EventBusSrv(),
instanceState: {},
});
expect(editorContext.data).toEqual([]);
});
it('returns suggestions for empty data', () => {
const editorContext = getStandardEditorContext({
data: undefined,
replaceVariables: jest.fn(),
options: {},
eventBus: new EventBusSrv(),
instanceState: {},
});
expect(editorContext.getSuggestions).toBeDefined();
expect(editorContext.getSuggestions?.()).toEqual([
{
documentation: 'Name of the series',
label: 'Name',
origin: 'series',
value: '__series.name',
},
{
documentation: 'Field name of the clicked datapoint (in ms epoch)',
label: 'Name',
origin: 'field',
value: '__field.name',
},
{
documentation: 'Adds current variables',
label: 'All variables',
origin: 'template',
value: '__all_variables',
},
{
documentation: 'Adds current time range',
label: 'Time range',
origin: 'built-in',
value: '__url_time_range',
},
{
documentation: "Adds current time range's from value",
label: 'Time range: from',
origin: 'built-in',
value: '__from',
},
{
documentation: "Adds current time range's to value",
label: 'Time range: to',
origin: 'built-in',
value: '__to',
},
]);
});
it('returns suggestions for non-empty data', () => {
const series = [
toDataFrame({
fields: [
{ name: 'time', type: FieldType.time },
{ name: 'score', type: FieldType.number },
],
}),
];
const panelData = {
series,
timeRange: getDefaultTimeRange(),
state: LoadingState.Done,
};
const editorContext = getStandardEditorContext({
data: panelData,
replaceVariables: jest.fn(),
options: {},
eventBus: new EventBusSrv(),
instanceState: {},
});
expect(editorContext.getSuggestions).toBeDefined();
expect(editorContext.getSuggestions?.()).toEqual([
{
documentation: 'Name of the series',
label: 'Name',
origin: 'series',
value: '__series.name',
},
{
documentation: 'Field name of the clicked datapoint (in ms epoch)',
label: 'Name',
origin: 'field',
value: '__field.name',
},
{
documentation: 'Formatted value for time on the same row',
label: 'time',
origin: 'fields',
value: '__data.fields.time',
},
{
documentation: 'Formatted value for score on the same row',
label: 'score',
origin: 'fields',
value: '__data.fields.score',
},
{
documentation: 'Enter the field order',
label: 'Select by index',
origin: 'fields',
value: '__data.fields[0]',
},
{
documentation: 'the numeric field value',
label: 'Show numeric value',
origin: 'fields',
value: '__data.fields.score.numeric',
},
{
documentation: 'the text value',
label: 'Show text value',
origin: 'fields',
value: '__data.fields.score.text',
},
{
documentation: 'Adds current variables',
label: 'All variables',
origin: 'template',
value: '__all_variables',
},
{
documentation: 'Adds current time range',
label: 'Time range',
origin: 'built-in',
value: '__url_time_range',
},
{
documentation: "Adds current time range's from value",
label: 'Time range: from',
origin: 'built-in',
value: '__from',
},
{
documentation: "Adds current time range's to value",
label: 'Time range: to',
origin: 'built-in',
value: '__to',
},
]);
});
});

View File

@ -1,9 +1,15 @@
import React from 'react';
import { StandardEditorContext, VariableSuggestionsScope } from '@grafana/data';
import {
EventBus,
InterpolateFunction,
PanelData,
StandardEditorContext,
VariableSuggestionsScope,
} from '@grafana/data';
import { get as lodashGet } from 'lodash';
import { getDataLinksVariableSuggestions } from 'app/features/panel/panellinks/link_srv';
import { OptionPaneRenderProps } from './types';
import { updateDefaultFieldConfigValue, setOptionImmutably } from './utils';
import { setOptionImmutably, updateDefaultFieldConfigValue } from './utils';
import { OptionsPaneItemDescriptor } from './OptionsPaneItemDescriptor';
import { OptionsPaneCategoryDescriptor } from './OptionsPaneCategoryDescriptor';
import {
@ -16,22 +22,48 @@ import { getOptionOverrides } from './state/getOptionOverrides';
type categoryGetter = (categoryNames?: string[]) => OptionsPaneCategoryDescriptor;
export function getVizualizationOptions(props: OptionPaneRenderProps): OptionsPaneCategoryDescriptor[] {
interface GetStandardEditorContextProps {
data: PanelData | undefined;
replaceVariables: InterpolateFunction;
options: Record<string, unknown>;
eventBus: EventBus;
instanceState: OptionPaneRenderProps['instanceState'];
}
export function getStandardEditorContext({
data,
replaceVariables,
options,
eventBus,
instanceState,
}: GetStandardEditorContextProps): StandardEditorContext<unknown, unknown> {
const dataSeries = data?.series ?? [];
const context: StandardEditorContext<unknown, unknown> = {
data: dataSeries,
replaceVariables,
options,
eventBus,
getSuggestions: (scope?: VariableSuggestionsScope) => getDataLinksVariableSuggestions(dataSeries, scope),
instanceState,
};
return context;
}
export function getVisualizationOptions(props: OptionPaneRenderProps): OptionsPaneCategoryDescriptor[] {
const { plugin, panel, onPanelOptionsChanged, onFieldConfigsChange, data, dashboard, instanceState } = props;
const currentOptions = panel.getOptions();
const currentFieldConfig = panel.fieldConfig;
const categoryIndex: Record<string, OptionsPaneCategoryDescriptor> = {};
const context: StandardEditorContext<any, any> = {
data: data?.series || [],
const context = getStandardEditorContext({
data,
replaceVariables: panel.replaceVariables,
options: currentOptions,
eventBus: dashboard.events,
getSuggestions: (scope?: VariableSuggestionsScope) => {
return data ? getDataLinksVariableSuggestions(data.series, scope) : [];
},
instanceState,
};
});
const getOptionsPaneCategory = (categoryNames?: string[]): OptionsPaneCategoryDescriptor => {
const categoryName = (categoryNames && categoryNames[0]) ?? `${plugin.meta.name}`;