mirror of
https://github.com/grafana/grafana.git
synced 2025-02-20 11:48:34 -06:00
TrendPanel: Add new trend panel (Alpha) (#65740)
This commit is contained in:
parent
313b3dd2af
commit
d974e5f25a
@ -6009,6 +6009,10 @@ exports[`better eslint`] = {
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/plugins/panel/trend/suggestions.ts:5381": [
|
||||
[0, 0, 0, "Do not use any type assertions.", "0"],
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
|
||||
],
|
||||
"public/app/plugins/panel/xychart/AutoEditor.tsx:5381": [
|
||||
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
|
||||
],
|
||||
|
1
.github/CODEOWNERS
vendored
1
.github/CODEOWNERS
vendored
@ -407,6 +407,7 @@ lerna.json @grafana/frontend-ops
|
||||
/public/app/plugins/panel/table/ @grafana/grafana-bi-squad
|
||||
/public/app/plugins/panel/table-old/ @grafana/grafana-bi-squad
|
||||
/public/app/plugins/panel/timeseries/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/trend/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/geomap/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/canvas/ @grafana/dataviz-squad
|
||||
/public/app/plugins/panel/candlestick/ @grafana/dataviz-squad
|
||||
|
@ -156,6 +156,11 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
|
||||
|
||||
let minMax: uPlot.Range.MinMax = [dataMin, dataMax];
|
||||
|
||||
// don't pad numeric x scales
|
||||
if (scaleKey === 'x' && !isTime) {
|
||||
return minMax;
|
||||
}
|
||||
|
||||
// happens when all series on a scale are `show: false`, re-returning nulls will auto-disable axis
|
||||
if (!hasFixedRange && dataMin == null && dataMax == null) {
|
||||
return minMax;
|
||||
|
@ -189,6 +189,7 @@ func verifyCorePluginCatalogue(t *testing.T, ctx context.Context, ps *store.Serv
|
||||
"table-old": {},
|
||||
"text": {},
|
||||
"timeseries": {},
|
||||
"trend": {},
|
||||
"welcome": {},
|
||||
"xychart": {},
|
||||
}
|
||||
|
@ -79,6 +79,7 @@ func corePlugins(rt *thema.Runtime) []pfs.ParsedPlugin {
|
||||
parsePluginOrPanic("public/app/plugins/panel/text", "text", rt),
|
||||
parsePluginOrPanic("public/app/plugins/panel/timeseries", "timeseries", rt),
|
||||
parsePluginOrPanic("public/app/plugins/panel/traces", "traces", rt),
|
||||
parsePluginOrPanic("public/app/plugins/panel/trend", "trend", rt),
|
||||
parsePluginOrPanic("public/app/plugins/panel/welcome", "welcome", rt),
|
||||
parsePluginOrPanic("public/app/plugins/panel/xychart", "xychart", rt),
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ seqs: [
|
||||
// grafana.com, then the plugin `id` has to follow the naming
|
||||
// conventions.
|
||||
id: string & strings.MinRunes(1)
|
||||
id: =~"^([0-9a-z]+\\-([0-9a-z]+\\-)?(\(strings.Join([ for t in _types {t}], "|"))))|(alertGroups|alertlist|annolist|barchart|bargauge|candlestick|canvas|dashlist|debug|gauge|geomap|gettingstarted|graph|heatmap|histogram|icon|live|logs|news|nodeGraph|piechart|pluginlist|stat|state-timeline|status-history|table|table-old|text|timeseries|traces|welcome|xychart|alertmanager|cloudwatch|dashboard|elasticsearch|grafana|grafana-azure-monitor-datasource|graphite|influxdb|jaeger|loki|mixed|mssql|mysql|opentsdb|postgres|prometheus|stackdriver|tempo|testdata|zipkin|phlare|parca)$"
|
||||
id: =~"^([0-9a-z]+\\-([0-9a-z]+\\-)?(\(strings.Join([ for t in _types {t}], "|"))))|(alertGroups|alertlist|annolist|barchart|bargauge|candlestick|canvas|dashlist|debug|gauge|geomap|gettingstarted|graph|heatmap|histogram|icon|live|logs|news|nodeGraph|piechart|pluginlist|stat|state-timeline|status-history|table|table-old|text|timeseries|trend|traces|welcome|xychart|alertmanager|cloudwatch|dashboard|elasticsearch|grafana|grafana-azure-monitor-datasource|graphite|influxdb|jaeger|loki|mixed|mssql|mysql|opentsdb|postgres|prometheus|stackdriver|tempo|testdata|zipkin|phlare|parca)$"
|
||||
|
||||
// Human-readable name of the plugin that is shown to the user in
|
||||
// the UI.
|
||||
|
@ -66,6 +66,7 @@ import * as tablePanel from 'app/plugins/panel/table/module';
|
||||
import * as textPanel from 'app/plugins/panel/text/module';
|
||||
import * as timeseriesPanel from 'app/plugins/panel/timeseries/module';
|
||||
import * as tracesPanel from 'app/plugins/panel/traces/module';
|
||||
import * as trendPanel from 'app/plugins/panel/trend/module';
|
||||
import * as welcomeBanner from 'app/plugins/panel/welcome/module';
|
||||
import * as xyChartPanel from 'app/plugins/panel/xychart/module';
|
||||
|
||||
@ -106,6 +107,7 @@ const builtInPlugins: any = {
|
||||
|
||||
'app/plugins/panel/text/module': textPanel,
|
||||
'app/plugins/panel/timeseries/module': timeseriesPanel,
|
||||
'app/plugins/panel/trend/module': trendPanel,
|
||||
'app/plugins/panel/state-timeline/module': stateTimelinePanel,
|
||||
'app/plugins/panel/status-history/module': statusHistoryPanel,
|
||||
'app/plugins/panel/candlestick/module': candlestickPanel,
|
||||
|
@ -1,15 +1,14 @@
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { GraphFieldConfig } from '@grafana/schema';
|
||||
import { commonOptionsBuilder } from '@grafana/ui';
|
||||
|
||||
import { TimeSeriesPanel } from './TimeSeriesPanel';
|
||||
import { TimezonesEditor } from './TimezonesEditor';
|
||||
import { defaultGraphConfig, getGraphFieldConfig } from './config';
|
||||
import { graphPanelChangedHandler } from './migrations';
|
||||
import { PanelOptions } from './panelcfg.gen';
|
||||
import { PanelFieldConfig, PanelOptions } from './panelcfg.gen';
|
||||
import { TimeSeriesSuggestionsSupplier } from './suggestions';
|
||||
|
||||
export const plugin = new PanelPlugin<PanelOptions, GraphFieldConfig>(TimeSeriesPanel)
|
||||
export const plugin = new PanelPlugin<PanelOptions, PanelFieldConfig>(TimeSeriesPanel)
|
||||
.setPanelChangeHandler(graphPanelChangedHandler)
|
||||
.useFieldConfig(getGraphFieldConfig(defaultGraphConfig))
|
||||
.setPanelOptions((builder) => {
|
||||
|
@ -22,12 +22,26 @@ import { nullToValue } from '@grafana/ui/src/components/GraphNG/nullToValue';
|
||||
export function prepareGraphableFields(
|
||||
series: DataFrame[],
|
||||
theme: GrafanaTheme2,
|
||||
timeRange?: TimeRange
|
||||
timeRange?: TimeRange,
|
||||
// numeric X requires a single frame where the first field is numeric
|
||||
xNumFieldIdx?: number
|
||||
): DataFrame[] | null {
|
||||
if (!series?.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let useNumericX = xNumFieldIdx != null;
|
||||
|
||||
// Make sure the numeric x field is first in the frame
|
||||
if (xNumFieldIdx != null && xNumFieldIdx > 0) {
|
||||
series = [
|
||||
{
|
||||
...series[0],
|
||||
fields: [series[0].fields[xNumFieldIdx], ...series[0].fields.filter((f, i) => i !== xNumFieldIdx)],
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
// some datasources simply tag the field as time, but don't convert to milli epochs
|
||||
// so we're stuck with doing the parsing here to avoid Moment slowness everywhere later
|
||||
// this mutates (once)
|
||||
@ -49,20 +63,26 @@ export function prepareGraphableFields(
|
||||
let hasTimeField = false;
|
||||
let hasValueField = false;
|
||||
|
||||
let nulledFrame = applyNullInsertThreshold({
|
||||
frame,
|
||||
refFieldPseudoMin: timeRange?.from.valueOf(),
|
||||
refFieldPseudoMax: timeRange?.to.valueOf(),
|
||||
});
|
||||
let nulledFrame = useNumericX
|
||||
? frame
|
||||
: applyNullInsertThreshold({
|
||||
frame,
|
||||
refFieldPseudoMin: timeRange?.from.valueOf(),
|
||||
refFieldPseudoMax: timeRange?.to.valueOf(),
|
||||
});
|
||||
|
||||
const frameFields = nullToValue(nulledFrame).fields;
|
||||
|
||||
for (let fieldIdx = 0; fieldIdx < frameFields?.length ?? 0; fieldIdx++) {
|
||||
const field = frameFields[fieldIdx];
|
||||
|
||||
for (const field of nullToValue(nulledFrame).fields) {
|
||||
switch (field.type) {
|
||||
case FieldType.time:
|
||||
hasTimeField = true;
|
||||
fields.push(field);
|
||||
break;
|
||||
case FieldType.number:
|
||||
hasValueField = true;
|
||||
hasValueField = useNumericX ? fieldIdx > 0 : true;
|
||||
copy = {
|
||||
...field,
|
||||
values: new ArrayVector(
|
||||
@ -124,7 +144,7 @@ export function prepareGraphableFields(
|
||||
}
|
||||
}
|
||||
|
||||
if (hasTimeField && hasValueField) {
|
||||
if ((useNumericX || hasTimeField) && hasValueField) {
|
||||
frames.push({
|
||||
...frame,
|
||||
length: nulledFrame.length,
|
||||
@ -134,20 +154,19 @@ export function prepareGraphableFields(
|
||||
}
|
||||
|
||||
if (frames.length) {
|
||||
setClassicPaletteIdxs(frames, theme);
|
||||
setClassicPaletteIdxs(frames, theme, 0);
|
||||
return frames;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const setClassicPaletteIdxs = (frames: DataFrame[], theme: GrafanaTheme2) => {
|
||||
const setClassicPaletteIdxs = (frames: DataFrame[], theme: GrafanaTheme2, skipFieldIdx?: number) => {
|
||||
let seriesIndex = 0;
|
||||
|
||||
frames.forEach((frame) => {
|
||||
frame.fields.forEach((field) => {
|
||||
frame.fields.forEach((field, fieldIdx) => {
|
||||
// TODO: also add FieldType.enum type here after https://github.com/grafana/grafana/pull/60491
|
||||
if (field.type === FieldType.number || field.type === FieldType.boolean) {
|
||||
if (fieldIdx !== skipFieldIdx && (field.type === FieldType.number || field.type === FieldType.boolean)) {
|
||||
field.state = {
|
||||
...field.state,
|
||||
seriesIndex: seriesIndex++, // TODO: skip this for fields with custom renderers (e.g. Candlestick)?
|
||||
|
137
public/app/plugins/panel/trend/TrendPanel.tsx
Normal file
137
public/app/plugins/panel/trend/TrendPanel.tsx
Normal file
@ -0,0 +1,137 @@
|
||||
import React, { useMemo } from 'react';
|
||||
|
||||
import { FieldType, PanelProps } from '@grafana/data';
|
||||
import { config, PanelDataErrorView } from '@grafana/runtime';
|
||||
import { KeyboardPlugin, TimeSeries, TooltipDisplayMode, TooltipPlugin, usePanelContext } from '@grafana/ui';
|
||||
import { findFieldIndex } from 'app/features/dimensions';
|
||||
|
||||
import { ContextMenuPlugin } from '../timeseries/plugins/ContextMenuPlugin';
|
||||
import { prepareGraphableFields, regenerateLinksSupplier } from '../timeseries/utils';
|
||||
|
||||
import { PanelOptions } from './panelcfg.gen';
|
||||
|
||||
export const TrendPanel = ({
|
||||
data,
|
||||
timeRange,
|
||||
timeZone,
|
||||
width,
|
||||
height,
|
||||
options,
|
||||
fieldConfig,
|
||||
replaceVariables,
|
||||
id,
|
||||
}: PanelProps<PanelOptions>) => {
|
||||
const { sync } = usePanelContext();
|
||||
|
||||
const info = useMemo(() => {
|
||||
if (data.series.length > 1) {
|
||||
return {
|
||||
warning: 'Only one frame is supported, consider adding a join transformation',
|
||||
frames: data.series,
|
||||
};
|
||||
}
|
||||
|
||||
let frames = data.series;
|
||||
let xFieldIdx: number | undefined;
|
||||
if (options.xField) {
|
||||
xFieldIdx = findFieldIndex(frames[0], options.xField);
|
||||
if (xFieldIdx == null) {
|
||||
return {
|
||||
warning: 'Unable to find field: ' + options.xField,
|
||||
frames: data.series,
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// first number field
|
||||
// Perhaps we can/should support any ordinal rather than an error here
|
||||
xFieldIdx = frames[0].fields.findIndex((f) => f.type === FieldType.number);
|
||||
if (xFieldIdx === -1) {
|
||||
return {
|
||||
warning: 'No numeric fields found for X axis',
|
||||
frames,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure values are ascending
|
||||
if (xFieldIdx != null) {
|
||||
const field = frames[0].fields[xFieldIdx];
|
||||
if (field.type === FieldType.number) {
|
||||
// we may support ordinal soon
|
||||
let last = Number.NEGATIVE_INFINITY;
|
||||
const values = field.values.toArray();
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const v = values[i];
|
||||
if (last > v) {
|
||||
return {
|
||||
warning: `Values must be in ascending order (index: ${i}, ${last} > ${v})`,
|
||||
frames,
|
||||
};
|
||||
}
|
||||
last = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { frames: prepareGraphableFields(frames, config.theme2, undefined, xFieldIdx) };
|
||||
}, [data, options.xField]);
|
||||
|
||||
if (info.warning || !info.frames) {
|
||||
return (
|
||||
<PanelDataErrorView
|
||||
panelId={id}
|
||||
fieldConfig={fieldConfig}
|
||||
data={data}
|
||||
message={info.warning}
|
||||
needsNumberField={true}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TimeSeries // Name change!
|
||||
frames={info.frames}
|
||||
structureRev={data.structureRev}
|
||||
timeRange={timeRange}
|
||||
timeZone={timeZone}
|
||||
width={width}
|
||||
height={height}
|
||||
legend={options.legend}
|
||||
options={options}
|
||||
>
|
||||
{(config, alignedDataFrame) => {
|
||||
if (
|
||||
alignedDataFrame.fields.filter((f) => f.config.links !== undefined && f.config.links.length > 0).length > 0
|
||||
) {
|
||||
alignedDataFrame = regenerateLinksSupplier(alignedDataFrame, info.frames!, replaceVariables, timeZone);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<KeyboardPlugin config={config} />
|
||||
{options.tooltip.mode === TooltipDisplayMode.None || (
|
||||
<TooltipPlugin
|
||||
frames={info.frames!}
|
||||
data={alignedDataFrame}
|
||||
config={config}
|
||||
mode={options.tooltip.mode}
|
||||
sortOrder={options.tooltip.sort}
|
||||
sync={sync}
|
||||
timeZone={timeZone}
|
||||
/>
|
||||
)}
|
||||
|
||||
<ContextMenuPlugin
|
||||
data={alignedDataFrame}
|
||||
frames={info.frames!}
|
||||
config={config}
|
||||
timeZone={timeZone}
|
||||
replaceVariables={replaceVariables}
|
||||
defaultItems={[]}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}}
|
||||
</TimeSeries>
|
||||
);
|
||||
};
|
9
public/app/plugins/panel/trend/img/trend.svg
Normal file
9
public/app/plugins/panel/trend/img/trend.svg
Normal file
@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 82.48 77.59">
|
||||
<defs><style>.cls-1{fill:url(#linear-gradient);}.cls-2{fill:url(#linear-gradient-2);}.cls-3{fill:#84aff1;}</style>
|
||||
<linearGradient id="linear-gradient" y1="12" x2="82.48" y2="12" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#f2cc0c"/><stop offset="1" stop-color="#ff9830"/></linearGradient><linearGradient id="linear-gradient-2" x1="41.14" y1="77.98" x2="41.91" y2="13.73" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1f60c4" stop-opacity="0"/><stop offset="1" stop-color="#3865ab"/></linearGradient>
|
||||
</defs>
|
||||
<g id="Icons">
|
||||
<path class="cls-1" d="M33.26,24a2,2,0,0,1-.83-.18l-14.7-6.66-15,5.71a2,2,0,1,1-1.42-3.74l15.8-6a2,2,0,0,1,1.53,0l14.16,6.41,15-16A2,2,0,0,1,50,3.16L65.15,9.73,79.38.33a2,2,0,1,1,2.2,3.34l-15.13,10a2,2,0,0,1-1.9.16L49.72,7.39l-15,16A2,2,0,0,1,33.26,24Z"/>
|
||||
<path class="cls-3" d="M2.11,45.72H2A2,2,0,0,1,.73,42.18l15.8-13a2,2,0,0,1,1.65-.42l14.59,2.83L48,20.12a2,2,0,0,1,2.4,0l14.7,10.94L79,16.35a2,2,0,1,1,2.9,2.75L66.8,35.1a2,2,0,0,1-2.64.23L49.23,24.22,34.46,35.32a2,2,0,0,1-1.58.37L18.34,32.87l-14.6,12A2,2,0,0,1,2.11,45.72Z"/>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
30
public/app/plugins/panel/trend/module.tsx
Normal file
30
public/app/plugins/panel/trend/module.tsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { PanelPlugin } from '@grafana/data';
|
||||
import { commonOptionsBuilder } from '@grafana/ui';
|
||||
|
||||
import { defaultGraphConfig, getGraphFieldConfig } from '../timeseries/config';
|
||||
|
||||
import { TrendPanel } from './TrendPanel';
|
||||
import { PanelFieldConfig, PanelOptions } from './panelcfg.gen';
|
||||
import { TrendSuggestionsSupplier } from './suggestions';
|
||||
|
||||
export const plugin = new PanelPlugin<PanelOptions, PanelFieldConfig>(TrendPanel)
|
||||
.useFieldConfig(getGraphFieldConfig(defaultGraphConfig))
|
||||
.setPanelOptions((builder) => {
|
||||
const category = ['X Axis'];
|
||||
builder.addFieldNamePicker({
|
||||
path: 'xField',
|
||||
name: 'X Field',
|
||||
description: 'An increasing numeric value',
|
||||
category,
|
||||
defaultValue: undefined,
|
||||
settings: {
|
||||
isClearable: true,
|
||||
placeholderText: 'First numeric value',
|
||||
},
|
||||
});
|
||||
|
||||
commonOptionsBuilder.addTooltipOptions(builder);
|
||||
commonOptionsBuilder.addLegendOptions(builder);
|
||||
})
|
||||
.setSuggestionsSupplier(new TrendSuggestionsSupplier());
|
||||
//.setDataSupport({ annotations: true, alertStates: true });
|
42
public/app/plugins/panel/trend/panelcfg.cue
Normal file
42
public/app/plugins/panel/trend/panelcfg.cue
Normal file
@ -0,0 +1,42 @@
|
||||
// Copyright 2021 Grafana Labs
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package grafanaplugin
|
||||
|
||||
import (
|
||||
"github.com/grafana/grafana/packages/grafana-schema/src/common"
|
||||
)
|
||||
|
||||
composableKinds: PanelCfg: {
|
||||
lineage: {
|
||||
seqs: [
|
||||
{
|
||||
schemas: [
|
||||
{
|
||||
// Identical to timeseries... except it does not have timezone settings
|
||||
PanelOptions: {
|
||||
legend: common.VizLegendOptions
|
||||
tooltip: common.VizTooltipOptions
|
||||
|
||||
// Name of the x field to use (defaults to first number)
|
||||
xField?: string
|
||||
} @cuetsy(kind="interface")
|
||||
|
||||
PanelFieldConfig: common.GraphFieldConfig & {} @cuetsy(kind="interface")
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
27
public/app/plugins/panel/trend/panelcfg.gen.ts
Normal file
27
public/app/plugins/panel/trend/panelcfg.gen.ts
Normal file
@ -0,0 +1,27 @@
|
||||
// Code generated - EDITING IS FUTILE. DO NOT EDIT.
|
||||
//
|
||||
// Generated by:
|
||||
// public/app/plugins/gen.go
|
||||
// Using jennies:
|
||||
// TSTypesJenny
|
||||
// PluginTSTypesJenny
|
||||
//
|
||||
// Run 'make gen-cue' from repository root to regenerate.
|
||||
|
||||
import * as common from '@grafana/schema';
|
||||
|
||||
export const PanelCfgModelVersion = Object.freeze([0, 0]);
|
||||
|
||||
/**
|
||||
* Identical to timeseries... except it does not have timezone settings
|
||||
*/
|
||||
export interface PanelOptions {
|
||||
legend: common.VizLegendOptions;
|
||||
tooltip: common.VizTooltipOptions;
|
||||
/**
|
||||
* Name of the x field to use (defaults to first number)
|
||||
*/
|
||||
xField?: string;
|
||||
}
|
||||
|
||||
export interface PanelFieldConfig extends common.GraphFieldConfig {}
|
19
public/app/plugins/panel/trend/plugin.json
Normal file
19
public/app/plugins/panel/trend/plugin.json
Normal file
@ -0,0 +1,19 @@
|
||||
{
|
||||
"type": "panel",
|
||||
"name": "Trend",
|
||||
"id": "trend",
|
||||
|
||||
"state": "alpha",
|
||||
|
||||
"info": {
|
||||
"description": "Like timeseries, but when x != time",
|
||||
"author": {
|
||||
"name": "Grafana Labs",
|
||||
"url": "https://grafana.com"
|
||||
},
|
||||
"logos": {
|
||||
"small": "img/trend.svg",
|
||||
"large": "img/trend.svg"
|
||||
}
|
||||
}
|
||||
}
|
40
public/app/plugins/panel/trend/suggestions.ts
Normal file
40
public/app/plugins/panel/trend/suggestions.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { VisualizationSuggestionsBuilder } from '@grafana/data';
|
||||
import { GraphDrawStyle, GraphFieldConfig } from '@grafana/schema';
|
||||
import { SuggestionName } from 'app/types/suggestions';
|
||||
|
||||
import { PanelOptions } from './panelcfg.gen';
|
||||
|
||||
export class TrendSuggestionsSupplier {
|
||||
getSuggestionsForData(builder: VisualizationSuggestionsBuilder) {
|
||||
const { dataSummary } = builder;
|
||||
|
||||
if (dataSummary.numberFieldCount < 2 || dataSummary.rowCountTotal < 2 || dataSummary.rowCountTotal < 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Super basic
|
||||
const list = builder.getListAppender<PanelOptions, GraphFieldConfig>({
|
||||
name: SuggestionName.LineChart,
|
||||
pluginId: 'trend',
|
||||
options: {
|
||||
legend: {} as any,
|
||||
},
|
||||
fieldConfig: {
|
||||
defaults: {
|
||||
custom: {},
|
||||
},
|
||||
overrides: [],
|
||||
},
|
||||
cardOptions: {
|
||||
previewModifier: (s) => {
|
||||
s.options!.legend.showLegend = false;
|
||||
|
||||
if (s.fieldConfig?.defaults.custom?.drawStyle !== GraphDrawStyle.Bars) {
|
||||
s.fieldConfig!.defaults.custom!.lineWidth = Math.max(s.fieldConfig!.defaults.custom!.lineWidth ?? 1, 2);
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
||||
return list;
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user