Refactor state timeline/status history to cue model and refactor TimelineChart component (#61631)

* Adapt state timeline to scuemata

* Refactor status history to cue model

* Refactor

* Refactor TimelineChart as a core component

* wip

* Change as per CR

Co-authored-by: sam boyer <sdboyer@grafana.com>
This commit is contained in:
Victor Marin 2023-01-26 09:03:59 +02:00 committed by GitHub
parent 6c9174a766
commit 6a93c77082
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 376 additions and 216 deletions

View File

@ -2476,6 +2476,24 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"] [0, 0, 0, "Unexpected any. Specify a different type.", "2"]
], ],
"public/app/core/components/TimelineChart/TimelineChart.tsx:5381": [
[0, 0, 0, "Do not use any type assertions.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/core/components/TimelineChart/timeline.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"]
],
"public/app/core/components/TimelineChart/utils.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/core/components/TimelineChart/utils.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"]
],
"public/app/core/components/connectWithCleanUp.tsx:5381": [ "public/app/core/components/connectWithCleanUp.tsx:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Do not use any type assertions.", "1"] [0, 0, 0, "Do not use any type assertions.", "1"]
@ -7510,10 +7528,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Do not use any type assertions.", "2"] [0, 0, 0, "Do not use any type assertions.", "2"]
], ],
"public/app/plugins/panel/state-timeline/TimelineChart.tsx: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/state-timeline/migrations.ts:5381": [ "public/app/plugins/panel/state-timeline/migrations.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"],
@ -7524,22 +7538,6 @@ exports[`better eslint`] = {
[0, 0, 0, "Do not use any type assertions.", "6"], [0, 0, 0, "Do not use any type assertions.", "6"],
[0, 0, 0, "Do not use any type assertions.", "7"] [0, 0, 0, "Do not use any type assertions.", "7"]
], ],
"public/app/plugins/panel/state-timeline/timeline.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"],
[0, 0, 0, "Unexpected any. Specify a different type.", "2"],
[0, 0, 0, "Do not use any type assertions.", "3"],
[0, 0, 0, "Do not use any type assertions.", "4"]
],
"public/app/plugins/panel/state-timeline/types.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/panel/state-timeline/utils.test.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/panel/state-timeline/utils.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"]
],
"public/app/plugins/panel/table-old/column_options.ts:5381": [ "public/app/plugins/panel/table-old/column_options.ts:5381": [
[0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "0"],
[0, 0, 0, "Unexpected any. Specify a different type.", "1"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"],

View File

@ -0,0 +1,41 @@
---
keywords:
- grafana
- schema
title: StateTimelinePanelCfg kind
---
> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
# StateTimelinePanelCfg kind
## Maturity: experimental
## Version: 0.0
## Properties
| Property | Type | Required | Description |
|--------------------|-----------------------------|----------|-------------|
| `PanelFieldConfig` | [object](#panelfieldconfig) | **Yes** | |
| `PanelOptions` | [object](#paneloptions) | **Yes** | |
## PanelFieldConfig
### Properties
| Property | Type | Required | Description |
|---------------|---------|----------|----------------|
| `fillOpacity` | integer | No | Default: `70`. |
| `lineWidth` | integer | No | Default: `0`. |
## PanelOptions
### Properties
| Property | Type | Required | Description |
|---------------|---------|----------|-------------------------------------------------------------------------------------------------------------|
| `alignValue` | string | No | Controls the value alignment in the TimelineChart component Possible values are: `center`, `left`, `right`. |
| `mergeValues` | boolean | No | Merge equal consecutive values Default: `true`. |
| `rowHeight` | number | No | Controls the row height Default: `0.9`. |
| `showValue` | string | No | TODO docs Possible values are: `auto`, `never`, `always`. |

View File

@ -0,0 +1,39 @@
---
keywords:
- grafana
- schema
title: StatusHistoryPanelCfg kind
---
> Both documentation generation and kinds schemas are in active development and subject to change without prior notice.
# StatusHistoryPanelCfg kind
## Maturity: experimental
## Version: 0.0
## Properties
| Property | Type | Required | Description |
|--------------------|-----------------------------|----------|-------------|
| `PanelFieldConfig` | [object](#panelfieldconfig) | **Yes** | |
| `PanelOptions` | [object](#paneloptions) | **Yes** | |
## PanelFieldConfig
### Properties
| Property | Type | Required | Description |
|---------------|---------|----------|----------------|
| `fillOpacity` | integer | No | Default: `70`. |
| `lineWidth` | integer | No | Default: `1`. |
## PanelOptions
### Properties
| Property | Type | Required | Description |
|-------------|--------|----------|-----------------------------------------------------------|
| `colWidth` | number | No | Controls the column width Default: `0.9`. |
| `showValue` | string | No | TODO docs Possible values are: `auto`, `never`, `always`. |

View File

@ -485,6 +485,11 @@ export enum BigValueTextMode {
*/ */
export type FieldTextAlignment = ('auto' | 'left' | 'right' | 'center'); export type FieldTextAlignment = ('auto' | 'left' | 'right' | 'center');
/**
* Controls the value alignment in the TimelineChart component
*/
export type TimelineValueAlignment = ('center' | 'left' | 'right');
/** /**
* TODO docs * TODO docs
*/ */

View File

@ -193,6 +193,9 @@ BigValueTextMode: "auto" | "value" | "value_and_name" | "name" | "none" @cuetsy(
// TODO docs // TODO docs
FieldTextAlignment: "auto" | "left" | "right" | "center" @cuetsy(kind="type") FieldTextAlignment: "auto" | "left" | "right" | "center" @cuetsy(kind="type")
// Controls the value alignment in the TimelineChart component
TimelineValueAlignment: "center" | "left" | "right" @cuetsy(kind="type")
// TODO docs // TODO docs
VizTextDisplayOptions: { VizTextDisplayOptions: {
// Explicit title text size // Explicit title text size

View File

@ -1349,6 +1349,30 @@
"pluralMachineName": "serviceaccounts", "pluralMachineName": "serviceaccounts",
"pluralName": "ServiceAccounts" "pluralName": "ServiceAccounts"
}, },
"statetimelinepanelcfg": {
"category": "composable",
"codeowners": [
"grafana/grafana-bi-squad"
],
"currentVersion": [
0,
0
],
"grafanaMaturityCount": 0,
"lineageIsGroup": true,
"links": {
"docs": "https://grafana.com/docs/grafana/next/developers/kinds/composable/statetimelinepanelcfg/schema-reference",
"go": "n/a",
"schema": "https://github.com/grafana/grafana/tree/main/public/app/plugins/panel/state-timeline/panelcfg.cue",
"ts": "https://github.com/grafana/grafana/tree/main/public/app/plugins/panel/state-timeline/panelcfg.gen.ts"
},
"machineName": "statetimelinepanelcfg",
"maturity": "experimental",
"name": "StateTimelinePanelCfg",
"pluralMachineName": "statetimelinepanelcfgs",
"pluralName": "StateTimelinePanelCfgs",
"schemaInterface": "PanelCfg"
},
"statpanelcfg": { "statpanelcfg": {
"category": "composable", "category": "composable",
"codeowners": [ "codeowners": [
@ -1373,6 +1397,30 @@
"pluralName": "StatPanelCfgs", "pluralName": "StatPanelCfgs",
"schemaInterface": "PanelCfg" "schemaInterface": "PanelCfg"
}, },
"statushistorypanelcfg": {
"category": "composable",
"codeowners": [
"grafana/grafana-bi-squad"
],
"currentVersion": [
0,
0
],
"grafanaMaturityCount": 0,
"lineageIsGroup": true,
"links": {
"docs": "https://grafana.com/docs/grafana/next/developers/kinds/composable/statushistorypanelcfg/schema-reference",
"go": "n/a",
"schema": "https://github.com/grafana/grafana/tree/main/public/app/plugins/panel/status-history/panelcfg.cue",
"ts": "https://github.com/grafana/grafana/tree/main/public/app/plugins/panel/status-history/panelcfg.gen.ts"
},
"machineName": "statushistorypanelcfg",
"maturity": "experimental",
"name": "StatusHistoryPanelCfg",
"pluralMachineName": "statushistorypanelcfgs",
"pluralName": "StatusHistoryPanelCfgs",
"schemaInterface": "PanelCfg"
},
"tableoldpanelcfg": { "tableoldpanelcfg": {
"category": "composable", "category": "composable",
"codeowners": [], "codeowners": [],
@ -1744,7 +1792,9 @@
"postgresqldatasourcecfg", "postgresqldatasourcecfg",
"prometheusdataquery", "prometheusdataquery",
"prometheusdatasourcecfg", "prometheusdatasourcecfg",
"statetimelinepanelcfg",
"statpanelcfg", "statpanelcfg",
"statushistorypanelcfg",
"tableoldpanelcfg", "tableoldpanelcfg",
"tempodataquery", "tempodataquery",
"tempodatasourcecfg", "tempodatasourcecfg",
@ -1757,7 +1807,7 @@
"zipkindataquery", "zipkindataquery",
"zipkindatasourcecfg" "zipkindatasourcecfg"
], ],
"count": 63 "count": 65
}, },
"core": { "core": {
"name": "core", "name": "core",
@ -1791,11 +1841,13 @@
"histogrampanelcfg", "histogrampanelcfg",
"newspanelcfg", "newspanelcfg",
"piechartpanelcfg", "piechartpanelcfg",
"statetimelinepanelcfg",
"statpanelcfg", "statpanelcfg",
"statushistorypanelcfg",
"textpanelcfg", "textpanelcfg",
"xychartpanelcfg" "xychartpanelcfg"
], ],
"count": 12 "count": 14
}, },
"mature": { "mature": {
"name": "mature", "name": "mature",

View File

@ -71,6 +71,8 @@ func corePlugins(rt *thema.Runtime) []pfs.ParsedPlugin {
parsePluginOrPanic("public/app/plugins/panel/nodeGraph", "nodeGraph", rt), parsePluginOrPanic("public/app/plugins/panel/nodeGraph", "nodeGraph", rt),
parsePluginOrPanic("public/app/plugins/panel/piechart", "piechart", rt), parsePluginOrPanic("public/app/plugins/panel/piechart", "piechart", rt),
parsePluginOrPanic("public/app/plugins/panel/stat", "stat", rt), parsePluginOrPanic("public/app/plugins/panel/stat", "stat", rt),
parsePluginOrPanic("public/app/plugins/panel/state-timeline", "state_timeline", rt),
parsePluginOrPanic("public/app/plugins/panel/status-history", "status_history", rt),
parsePluginOrPanic("public/app/plugins/panel/table-old", "table_old", rt), parsePluginOrPanic("public/app/plugins/panel/table-old", "table_old", rt),
parsePluginOrPanic("public/app/plugins/panel/text", "text", rt), parsePluginOrPanic("public/app/plugins/panel/text", "text", rt),
parsePluginOrPanic("public/app/plugins/panel/traces", "traces", rt), parsePluginOrPanic("public/app/plugins/panel/traces", "traces", rt),

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { DataFrame, FALLBACK_COLOR, FieldType, TimeRange } from '@grafana/data'; import { DataFrame, FALLBACK_COLOR, FieldType, TimeRange } from '@grafana/data';
import { VisibilityMode } from '@grafana/schema'; import { VisibilityMode, TimelineValueAlignment } from '@grafana/schema';
import { import {
PanelContext, PanelContext,
PanelContextRoot, PanelContextRoot,
@ -13,17 +13,14 @@ import {
VizLegendItem, VizLegendItem,
} from '@grafana/ui'; } from '@grafana/ui';
import { TimelineMode, TimelineOptions, TimelineValueAlignment } from './types'; import { preparePlotConfigBuilder, TimelineMode } from './utils';
import { preparePlotConfigBuilder } from './utils';
/** /**
* @alpha * @alpha
*/ */
export interface TimelineProps export interface TimelineProps extends Omit<GraphNGProps, 'prepConfig' | 'propsToDiff' | 'renderLegend'> {
extends TimelineOptions,
Omit<GraphNGProps, 'prepConfig' | 'propsToDiff' | 'renderLegend'> {
mode: TimelineMode; mode: TimelineMode;
rowHeight: number; rowHeight?: number;
showValue: VisibilityMode; showValue: VisibilityMode;
alignValue?: TimelineValueAlignment; alignValue?: TimelineValueAlignment;
colWidth?: number; colWidth?: number;

View File

@ -2,12 +2,14 @@ import uPlot, { Cursor, Series } from 'uplot';
import { GrafanaTheme2, TimeRange } from '@grafana/data'; import { GrafanaTheme2, TimeRange } from '@grafana/data';
import { alpha } from '@grafana/data/src/themes/colorManipulator'; import { alpha } from '@grafana/data/src/themes/colorManipulator';
import { VisibilityMode } from '@grafana/schema'; import { VisibilityMode, TimelineValueAlignment } from '@grafana/schema';
import { FIXED_UNIT } from '@grafana/ui/src/components/GraphNG/GraphNG'; import { FIXED_UNIT } from '@grafana/ui/src/components/GraphNG/GraphNG';
import { distribute, SPACE_BETWEEN } from 'app/plugins/panel/barchart/distribute'; import { distribute, SPACE_BETWEEN } from 'app/plugins/panel/barchart/distribute';
import { pointWithin, Quadtree, Rect } from 'app/plugins/panel/barchart/quadtree'; import { pointWithin, Quadtree, Rect } from 'app/plugins/panel/barchart/quadtree';
import { PanelFieldConfig as StateTimeLineFieldConfig } from 'app/plugins/panel/state-timeline/panelcfg.gen';
import { PanelFieldConfig as StatusHistoryFieldConfig } from 'app/plugins/panel/status-history/panelcfg.gen';
import { TimelineFieldConfig, TimelineMode, TimelineValueAlignment } from './types'; import { TimelineMode } from './utils';
const { round, min, ceil } = Math; const { round, min, ceil } = Math;
@ -39,7 +41,7 @@ export interface TimelineCoreOptions {
mode: TimelineMode; mode: TimelineMode;
alignValue?: TimelineValueAlignment; alignValue?: TimelineValueAlignment;
numSeries: number; numSeries: number;
rowHeight: number; rowHeight?: number;
colWidth?: number; colWidth?: number;
theme: GrafanaTheme2; theme: GrafanaTheme2;
showValue: VisibilityMode; showValue: VisibilityMode;
@ -49,7 +51,7 @@ export interface TimelineCoreOptions {
label: (seriesIdx: number) => string; label: (seriesIdx: number) => string;
getTimeRange: () => TimeRange; getTimeRange: () => TimeRange;
formatValue?: (seriesIdx: number, value: any) => string; formatValue?: (seriesIdx: number, value: any) => string;
getFieldConfig: (seriesIdx: number) => TimelineFieldConfig; getFieldConfig: (seriesIdx: number) => StateTimeLineFieldConfig | StatusHistoryFieldConfig;
onHover: (seriesIdx: number, valueIdx: number, rect: Rect) => void; onHover: (seriesIdx: number, valueIdx: number, rect: Rect) => void;
onLeave: () => void; onLeave: () => void;
} }
@ -567,7 +569,7 @@ export function getConfig(opts: TimelineCoreOptions) {
}; };
} }
function getFillColor(fieldConfig: TimelineFieldConfig, color: string) { function getFillColor(fieldConfig: { fillOpacity?: number; lineWidth?: number }, color: string) {
// if #rgba with pre-existing alpha. ignore fieldConfig.fillOpacity // if #rgba with pre-existing alpha. ignore fieldConfig.fillOpacity
// e.g. thresholds with opacity // e.g. thresholds with opacity
if (color[0] === '#' && color.length === 9) { if (color[0] === '#' && color.length === 9) {

View File

@ -24,7 +24,15 @@ import {
TimeRange, TimeRange,
} from '@grafana/data'; } from '@grafana/data';
import { maybeSortFrame } from '@grafana/data/src/transformations/transformers/joinDataFrames'; import { maybeSortFrame } from '@grafana/data/src/transformations/transformers/joinDataFrames';
import { VizLegendOptions, AxisPlacement, ScaleDirection, ScaleOrientation } from '@grafana/schema'; import {
VizLegendOptions,
AxisPlacement,
ScaleDirection,
ScaleOrientation,
VisibilityMode,
TimelineValueAlignment,
HideableFieldConfig,
} from '@grafana/schema';
import { import {
FIXED_UNIT, FIXED_UNIT,
SeriesVisibilityChangeMode, SeriesVisibilityChangeMode,
@ -38,9 +46,37 @@ import { PlotTooltipInterpolator } from '@grafana/ui/src/components/uPlot/types'
import { preparePlotData2, getStackingGroups } from '@grafana/ui/src/components/uPlot/utils'; import { preparePlotData2, getStackingGroups } from '@grafana/ui/src/components/uPlot/utils';
import { getConfig, TimelineCoreOptions } from './timeline'; import { getConfig, TimelineCoreOptions } from './timeline';
import { TimelineFieldConfig, TimelineOptions } from './types';
const defaultConfig: TimelineFieldConfig = { /**
* @internal
*/
interface UPlotConfigOptions {
frame: DataFrame;
theme: GrafanaTheme2;
mode: TimelineMode;
sync?: () => DashboardCursorSync;
rowHeight?: number;
colWidth?: number;
showValue: VisibilityMode;
alignValue?: TimelineValueAlignment;
mergeValues?: boolean;
getValueColor: (frameIdx: number, fieldIdx: number, value: any) => string;
}
/**
* @internal
*/
interface PanelFieldConfig extends HideableFieldConfig {
fillOpacity?: number;
lineWidth?: number;
}
export enum TimelineMode {
Changes = 'changes',
Samples = 'samples',
}
const defaultConfig: PanelFieldConfig = {
lineWidth: 0, lineWidth: 0,
fillOpacity: 80, fillOpacity: 80,
}; };
@ -52,7 +88,7 @@ export function mapMouseEventToMode(event: React.MouseEvent): SeriesVisibilityCh
return SeriesVisibilityChangeMode.ToggleSelection; return SeriesVisibilityChangeMode.ToggleSelection;
} }
export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({ export const preparePlotConfigBuilder: UPlotConfigPrepFn<UPlotConfigOptions> = ({
frame, frame,
theme, theme,
timeZones, timeZones,
@ -92,12 +128,11 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
}; };
const opts: TimelineCoreOptions = { const opts: TimelineCoreOptions = {
// should expose in panel config
mode: mode!, mode: mode!,
numSeries: frame.fields.length - 1, numSeries: frame.fields.length - 1,
isDiscrete: (seriesIdx) => isDiscrete(frame.fields[seriesIdx]), isDiscrete: (seriesIdx) => isDiscrete(frame.fields[seriesIdx]),
mergeValues, mergeValues,
rowHeight: rowHeight!, rowHeight: rowHeight,
colWidth: colWidth, colWidth: colWidth,
showValue: showValue!, showValue: showValue!,
alignValue, alignValue,
@ -209,8 +244,8 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<TimelineOptions> = ({
} }
const field = frame.fields[i]; const field = frame.fields[i];
const config: FieldConfig<TimelineFieldConfig> = field.config; const config: FieldConfig<PanelFieldConfig> = field.config;
const customConfig: TimelineFieldConfig = { const customConfig: PanelFieldConfig = {
...defaultConfig, ...defaultConfig,
...config.custom, ...config.custom,
}; };

View File

@ -21,16 +21,14 @@ import (
) )
var skipPlugins = map[string]bool{ var skipPlugins = map[string]bool{
"canvas": true, "canvas": true,
"heatmap": true, "heatmap": true,
"candlestick": true, "candlestick": true,
"state-timeline": true, "table": true,
"status-history": true, "timeseries": true,
"table": true, "influxdb": true, // plugin.json fails validation (defaultMatchFormat)
"timeseries": true, "mixed": true, // plugin.json fails validation (mixed)
"influxdb": true, // plugin.json fails validation (defaultMatchFormat) "opentsdb": true, // plugin.json fails validation (defaultMatchFormat)
"mixed": true, // plugin.json fails validation (mixed)
"opentsdb": true, // plugin.json fails validation (defaultMatchFormat)
} }
const sep = string(filepath.Separator) const sep = string(filepath.Separator)

View File

@ -33,9 +33,9 @@ import {
import { PropDiffFn } from '@grafana/ui/src/components/GraphNG/GraphNG'; import { PropDiffFn } from '@grafana/ui/src/components/GraphNG/GraphNG';
import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport'; import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport';
import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
import { getFieldLegendItem } from 'app/core/components/TimelineChart/utils';
import { DataHoverView } from '../geomap/components/DataHoverView'; import { DataHoverView } from '../geomap/components/DataHoverView';
import { getFieldLegendItem } from '../state-timeline/utils';
import { PanelOptions } from './panelcfg.gen'; import { PanelOptions } from './panelcfg.gen';
import { prepareBarChartDisplayValues, preparePlotConfigBuilder } from './utils'; import { prepareBarChartDisplayValues, preparePlotConfigBuilder } from './utils';

View File

@ -9,9 +9,9 @@ import { getMinMaxAndDelta } from '@grafana/data/src/field/scale';
import { useStyles2, VizLegendItem } from '@grafana/ui'; import { useStyles2, VizLegendItem } from '@grafana/ui';
import { ColorScale } from 'app/core/components/ColorScale/ColorScale'; import { ColorScale } from 'app/core/components/ColorScale/ColorScale';
import { SanitizedSVG } from 'app/core/components/SVG/SanitizedSVG'; import { SanitizedSVG } from 'app/core/components/SVG/SanitizedSVG';
import { getThresholdItems } from 'app/core/components/TimelineChart/utils';
import { config } from 'app/core/config'; import { config } from 'app/core/config';
import { DimensionSupplier } from 'app/features/dimensions'; import { DimensionSupplier } from 'app/features/dimensions';
import { getThresholdItems } from 'app/plugins/panel/state-timeline/utils';
import { StyleConfigState } from '../style/types'; import { StyleConfigState } from '../style/types';
import { MapLayerState } from '../types'; import { MapLayerState } from '../types';

View File

@ -12,6 +12,12 @@ import {
} from '@grafana/ui'; } from '@grafana/ui';
import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport'; import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport';
import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
import { TimelineChart } from 'app/core/components/TimelineChart/TimelineChart';
import {
prepareTimelineFields,
prepareTimelineLegendItems,
TimelineMode,
} from 'app/core/components/TimelineChart/utils';
import { getLastStreamingDataFramePacket } from 'app/features/live/data/StreamingDataFrame'; import { getLastStreamingDataFramePacket } from 'app/features/live/data/StreamingDataFrame';
import { AnnotationEditorPlugin } from '../timeseries/plugins/AnnotationEditorPlugin'; import { AnnotationEditorPlugin } from '../timeseries/plugins/AnnotationEditorPlugin';
@ -20,13 +26,11 @@ import { OutsideRangePlugin } from '../timeseries/plugins/OutsideRangePlugin';
import { getTimezones } from '../timeseries/utils'; import { getTimezones } from '../timeseries/utils';
import { StateTimelineTooltip } from './StateTimelineTooltip'; import { StateTimelineTooltip } from './StateTimelineTooltip';
import { TimelineChart } from './TimelineChart'; import { PanelOptions } from './panelcfg.gen';
import { TimelineMode, TimelineOptions } from './types';
import { prepareTimelineFields, prepareTimelineLegendItems } from './utils';
const TOOLTIP_OFFSET = 10; const TOOLTIP_OFFSET = 10;
interface TimelinePanelProps extends PanelProps<TimelineOptions> {} interface TimelinePanelProps extends PanelProps<PanelOptions> {}
/** /**
* @alpha * @alpha

View File

@ -10,8 +10,7 @@ import {
LinkModel, LinkModel,
} from '@grafana/data'; } from '@grafana/data';
import { MenuItem, SeriesTableRow, useTheme2 } from '@grafana/ui'; import { MenuItem, SeriesTableRow, useTheme2 } from '@grafana/ui';
import { findNextStateIndex, fmtDuration } from 'app/core/components/TimelineChart/utils';
import { findNextStateIndex, fmtDuration } from './utils';
interface StateTimelineTooltipProps { interface StateTimelineTooltipProps {
data: DataFrame[]; data: DataFrame[];

View File

@ -2,15 +2,15 @@ import { isArray } from 'lodash';
import { FieldConfigSource, MappingType, PanelModel, ValueMap } from '@grafana/data'; import { FieldConfigSource, MappingType, PanelModel, ValueMap } from '@grafana/data';
import { TimelineFieldConfig, TimelineOptions } from './types'; import { PanelFieldConfig, PanelOptions } from './panelcfg.gen';
// This is called when the panel changes from another panel // This is called when the panel changes from another panel
export const timelinePanelChangedHandler = ( export const timelinePanelChangedHandler = (
panel: PanelModel<Partial<TimelineOptions>> | any, panel: PanelModel<Partial<PanelOptions>> | any,
prevPluginId: string, prevPluginId: string,
prevOptions: any prevOptions: any
) => { ) => {
let options = (panel.options ?? {}) as TimelineOptions; let options = (panel.options ?? {}) as PanelOptions;
// Changing from angular singlestat // Changing from angular singlestat
if (prevPluginId === 'natel-discrete-panel' && prevOptions.angular) { if (prevPluginId === 'natel-discrete-panel' && prevOptions.angular) {
@ -21,7 +21,7 @@ export const timelinePanelChangedHandler = (
fieldConfig.defaults.unit = oldOptions.units; fieldConfig.defaults.unit = oldOptions.units;
} }
const custom: TimelineFieldConfig = { const custom: PanelFieldConfig = {
fillOpacity: 100, fillOpacity: 100,
lineWidth: 0, lineWidth: 0,
}; };

View File

@ -12,10 +12,10 @@ import { SpanNullsEditor } from '../timeseries/SpanNullsEditor';
import { StateTimelinePanel } from './StateTimelinePanel'; import { StateTimelinePanel } from './StateTimelinePanel';
import { timelinePanelChangedHandler } from './migrations'; import { timelinePanelChangedHandler } from './migrations';
import { PanelOptions, PanelFieldConfig, defaultPanelOptions, defaultPanelFieldConfig } from './panelcfg.gen';
import { StatTimelineSuggestionsSupplier } from './suggestions'; import { StatTimelineSuggestionsSupplier } from './suggestions';
import { TimelineOptions, TimelineFieldConfig, defaultPanelOptions, defaultTimelineFieldConfig } from './types';
export const plugin = new PanelPlugin<TimelineOptions, TimelineFieldConfig>(StateTimelinePanel) export const plugin = new PanelPlugin<PanelOptions, PanelFieldConfig>(StateTimelinePanel)
.setPanelChangeHandler(timelinePanelChangedHandler) .setPanelChangeHandler(timelinePanelChangedHandler)
.useFieldConfig({ .useFieldConfig({
standardOptions: { standardOptions: {
@ -33,7 +33,7 @@ export const plugin = new PanelPlugin<TimelineOptions, TimelineFieldConfig>(Stat
.addSliderInput({ .addSliderInput({
path: 'lineWidth', path: 'lineWidth',
name: 'Line width', name: 'Line width',
defaultValue: defaultTimelineFieldConfig.lineWidth, defaultValue: defaultPanelFieldConfig.lineWidth,
settings: { settings: {
min: 0, min: 0,
max: 10, max: 10,
@ -43,7 +43,7 @@ export const plugin = new PanelPlugin<TimelineOptions, TimelineFieldConfig>(Stat
.addSliderInput({ .addSliderInput({
path: 'fillOpacity', path: 'fillOpacity',
name: 'Fill opacity', name: 'Fill opacity',
defaultValue: defaultTimelineFieldConfig.fillOpacity, defaultValue: defaultPanelFieldConfig.fillOpacity,
settings: { settings: {
min: 0, min: 0,
max: 100, max: 100,

View File

@ -15,33 +15,34 @@
package grafanaplugin package grafanaplugin
import ( import (
"github.com/grafana/grafana/packages/grafana-schema/src/common" ui "github.com/grafana/grafana/packages/grafana-schema/src/common"
) )
composableKinds: PanelCfg: { composableKinds: PanelCfg: {
maturity: "experimental"
lineage: { lineage: {
seqs: [ seqs: [
{ {
schemas: [ schemas: [
{ {
TimelineMode: "changes" | "samples" @cuetsy(kind="enum")
TimelineValueAlignment: "center" | "left" | "right" @cuetsy(kind="type")
PanelOptions: { PanelOptions: {
// FIXME ts comments indicate this shouldn't be in the saved model, but currently is emitted ui.OptionsWithLegend
mode?: TimelineMode ui.OptionsWithTooltip
common.OptionsWithLegend ui.OptionsWithTimezones
common.OptionsWithTooltip
common.OptionsWithTimezones //Show timeline values on chart
showValue: common.VisibilityMode | *"auto" showValue: ui.VisibilityMode | *"auto"
rowHeight: number | *0.9 //Controls the row height
colWidth?: number rowHeight: float & <=1 | *0.9
//Merge equal consecutive values
mergeValues?: bool | *true mergeValues?: bool | *true
alignValue?: TimelineValueAlignment | *"left" //Controls value alignment on the timelines
alignValue?: ui.TimelineValueAlignment | *"left"
} @cuetsy(kind="interface") } @cuetsy(kind="interface")
PanelFieldConfig: { PanelFieldConfig: {
common.HideableFieldConfig ui.HideableFieldConfig
lineWidth?: number | *0 lineWidth?: uint32 & <=10 | *0
fillOpacity?: number | *70 fillOpacity?: uint32 & <=100 | *70
} @cuetsy(kind="interface") } @cuetsy(kind="interface")
}, },
] ]

View File

@ -0,0 +1,49 @@
// 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 ui from '@grafana/schema';
export const PanelCfgModelVersion = Object.freeze([0, 0]);
export interface PanelOptions extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones {
/**
* Controls value alignment on the timelines
*/
alignValue?: ui.TimelineValueAlignment;
/**
* Merge equal consecutive values
*/
mergeValues?: boolean;
/**
* Controls the row height
*/
rowHeight: number;
/**
* Show timeline values on chart
*/
showValue: ui.VisibilityMode;
}
export const defaultPanelOptions: Partial<PanelOptions> = {
alignValue: 'left',
mergeValues: true,
rowHeight: 0.9,
showValue: ui.VisibilityMode.Auto,
};
export interface PanelFieldConfig extends ui.HideableFieldConfig {
fillOpacity?: number;
lineWidth?: number;
}
export const defaultPanelFieldConfig: Partial<PanelFieldConfig> = {
fillOpacity: 70,
lineWidth: 0,
};

View File

@ -1,7 +1,7 @@
import { VisualizationSuggestionsBuilder } from '@grafana/data'; import { VisualizationSuggestionsBuilder } from '@grafana/data';
import { SuggestionName } from 'app/types/suggestions'; import { SuggestionName } from 'app/types/suggestions';
import { TimelineFieldConfig, TimelineOptions } from './types'; import { PanelFieldConfig, PanelOptions } from './panelcfg.gen';
export class StatTimelineSuggestionsSupplier { export class StatTimelineSuggestionsSupplier {
getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { getSuggestionsForData(builder: VisualizationSuggestionsBuilder) {
@ -26,7 +26,7 @@ export class StatTimelineSuggestionsSupplier {
return; return;
} }
const list = builder.getListAppender<TimelineOptions, TimelineFieldConfig>({ const list = builder.getListAppender<PanelOptions, PanelFieldConfig>({
name: '', name: '',
pluginId: 'state-timeline', pluginId: 'state-timeline',
options: {}, options: {},

View File

@ -1,66 +0,0 @@
import { DashboardCursorSync } from '@grafana/data';
import {
HideableFieldConfig,
OptionsWithLegend,
OptionsWithTimezones,
OptionsWithTooltip,
VisibilityMode,
} from '@grafana/schema';
/**
* @alpha
*/
export interface TimelineOptions extends OptionsWithLegend, OptionsWithTooltip, OptionsWithTimezones {
mode: TimelineMode; // not in the saved model!
showValue: VisibilityMode;
rowHeight: number;
// only used for "samples" mode (status-history)
colWidth?: number;
// only used in "changes" mode (state-timeline)
mergeValues?: boolean;
// only used in "changes" mode (state-timeline)
alignValue?: TimelineValueAlignment;
sync?: () => DashboardCursorSync;
getValueColor?: (frameIdx: number, fieldIdx: number, value: any) => string;
}
export type TimelineValueAlignment = 'center' | 'left' | 'right';
/**
* @alpha
*/
export interface TimelineFieldConfig extends HideableFieldConfig {
lineWidth?: number; // 0
fillOpacity?: number; // 100
}
/**
* @alpha
*/
export const defaultPanelOptions: Partial<TimelineOptions> = {
showValue: VisibilityMode.Auto,
alignValue: 'left',
mergeValues: true,
rowHeight: 0.9,
};
/**
* @alpha
*/
export const defaultTimelineFieldConfig: TimelineFieldConfig = {
lineWidth: 0,
fillOpacity: 70,
};
/**
* @alpha
*/
export enum TimelineMode {
// state-timeline
Changes = 'changes',
// status-history
Samples = 'samples',
}

View File

@ -12,19 +12,22 @@ import {
} from '@grafana/ui'; } from '@grafana/ui';
import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport'; import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport';
import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; import { CloseButton } from 'app/core/components/CloseButton/CloseButton';
import { TimelineChart } from 'app/core/components/TimelineChart/TimelineChart';
import {
prepareTimelineFields,
prepareTimelineLegendItems,
TimelineMode,
} from 'app/core/components/TimelineChart/utils';
import { TimelineChart } from '../state-timeline/TimelineChart';
import { TimelineMode } from '../state-timeline/types';
import { prepareTimelineFields, prepareTimelineLegendItems } from '../state-timeline/utils';
import { OutsideRangePlugin } from '../timeseries/plugins/OutsideRangePlugin'; import { OutsideRangePlugin } from '../timeseries/plugins/OutsideRangePlugin';
import { getTimezones } from '../timeseries/utils'; import { getTimezones } from '../timeseries/utils';
import { StatusHistoryTooltip } from './StatusHistoryTooltip'; import { StatusHistoryTooltip } from './StatusHistoryTooltip';
import { StatusPanelOptions } from './types'; import { PanelOptions } from './panelcfg.gen';
const TOOLTIP_OFFSET = 10; const TOOLTIP_OFFSET = 10;
interface TimelinePanelProps extends PanelProps<StatusPanelOptions> {} interface TimelinePanelProps extends PanelProps<PanelOptions> {}
/** /**
* @alpha * @alpha
@ -193,7 +196,6 @@ export const StatusHistoryPanel: React.FC<TimelinePanelProps> = ({
height={height} height={height}
legendItems={legendItems} legendItems={legendItems}
{...options} {...options}
// hardcoded
mode={TimelineMode.Samples} mode={TimelineMode.Samples}
> >
{(config, alignedFrame) => { {(config, alignedFrame) => {

View File

@ -3,10 +3,10 @@ import { VisibilityMode } from '@grafana/schema';
import { commonOptionsBuilder } from '@grafana/ui'; import { commonOptionsBuilder } from '@grafana/ui';
import { StatusHistoryPanel } from './StatusHistoryPanel'; import { StatusHistoryPanel } from './StatusHistoryPanel';
import { PanelOptions, PanelFieldConfig, defaultPanelFieldConfig } from './panelcfg.gen';
import { StatusHistorySuggestionsSupplier } from './suggestions'; import { StatusHistorySuggestionsSupplier } from './suggestions';
import { StatusPanelOptions, StatusFieldConfig, defaultStatusFieldConfig } from './types';
export const plugin = new PanelPlugin<StatusPanelOptions, StatusFieldConfig>(StatusHistoryPanel) export const plugin = new PanelPlugin<PanelOptions, PanelFieldConfig>(StatusHistoryPanel)
.useFieldConfig({ .useFieldConfig({
standardOptions: { standardOptions: {
[FieldConfigProperty.Color]: { [FieldConfigProperty.Color]: {
@ -23,7 +23,7 @@ export const plugin = new PanelPlugin<StatusPanelOptions, StatusFieldConfig>(Sta
.addSliderInput({ .addSliderInput({
path: 'lineWidth', path: 'lineWidth',
name: 'Line width', name: 'Line width',
defaultValue: defaultStatusFieldConfig.lineWidth, defaultValue: defaultPanelFieldConfig.lineWidth,
settings: { settings: {
min: 0, min: 0,
max: 10, max: 10,
@ -33,7 +33,7 @@ export const plugin = new PanelPlugin<StatusPanelOptions, StatusFieldConfig>(Sta
.addSliderInput({ .addSliderInput({
path: 'fillOpacity', path: 'fillOpacity',
name: 'Fill opacity', name: 'Fill opacity',
defaultValue: defaultStatusFieldConfig.fillOpacity, defaultValue: defaultPanelFieldConfig.fillOpacity,
settings: { settings: {
min: 0, min: 0,
max: 100, max: 100,
@ -56,16 +56,6 @@ export const plugin = new PanelPlugin<StatusPanelOptions, StatusFieldConfig>(Sta
}, },
defaultValue: VisibilityMode.Auto, defaultValue: VisibilityMode.Auto,
}) })
.addSliderInput({
path: 'rowHeight',
name: 'Row height',
defaultValue: 0.9,
settings: {
min: 0,
max: 1,
step: 0.01,
},
})
.addSliderInput({ .addSliderInput({
path: 'colWidth', path: 'colWidth',
name: 'Column width', name: 'Column width',

View File

@ -15,28 +15,30 @@
package grafanaplugin package grafanaplugin
import ( import (
"github.com/grafana/grafana/packages/grafana-schema/src/common" ui "github.com/grafana/grafana/packages/grafana-schema/src/common"
) )
composableKinds: PanelCfg: { composableKinds: PanelCfg: {
maturity: "experimental"
lineage: { lineage: {
seqs: [ seqs: [
{ {
schemas: [ schemas: [
{ {
PanelOptions: { PanelOptions: {
common.OptionsWithLegend ui.OptionsWithLegend
common.OptionsWithTooltip ui.OptionsWithTooltip
common.OptionsWithTimezones ui.OptionsWithTimezones
showValue: common.VisibilityMode
rowHeight: number //Show values on the columns
colWidth?: number showValue: ui.VisibilityMode | *"auto"
alignValue: "center" | *"left" | "right" //Controls the column width
colWidth?: float & <=1 | *0.9
} @cuetsy(kind="interface") } @cuetsy(kind="interface")
PanelFieldConfig: { PanelFieldConfig: {
common.HideableFieldConfig ui.HideableFieldConfig
lineWidth?: number | *1 lineWidth?: uint32 & <=10 | *1
fillOpacity?: number | *70 fillOpacity?: uint32 & <=100 | *70
} @cuetsy(kind="interface") } @cuetsy(kind="interface")
}, },
] ]

View File

@ -0,0 +1,39 @@
// 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 ui from '@grafana/schema';
export const PanelCfgModelVersion = Object.freeze([0, 0]);
export interface PanelOptions extends ui.OptionsWithLegend, ui.OptionsWithTooltip, ui.OptionsWithTimezones {
/**
* Controls the column width
*/
colWidth?: number;
/**
* Show values on the columns
*/
showValue: ui.VisibilityMode;
}
export const defaultPanelOptions: Partial<PanelOptions> = {
colWidth: 0.9,
showValue: ui.VisibilityMode.Auto,
};
export interface PanelFieldConfig extends ui.HideableFieldConfig {
fillOpacity?: number;
lineWidth?: number;
}
export const defaultPanelFieldConfig: Partial<PanelFieldConfig> = {
fillOpacity: 70,
lineWidth: 1,
};

View File

@ -1,7 +1,7 @@
import { FieldColorModeId, VisualizationSuggestionsBuilder } from '@grafana/data'; import { FieldColorModeId, VisualizationSuggestionsBuilder } from '@grafana/data';
import { SuggestionName } from 'app/types/suggestions'; import { SuggestionName } from 'app/types/suggestions';
import { StatusPanelOptions, StatusFieldConfig } from './types'; import { PanelOptions, PanelFieldConfig } from './panelcfg.gen';
export class StatusHistorySuggestionsSupplier { export class StatusHistorySuggestionsSupplier {
getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { getSuggestionsForData(builder: VisualizationSuggestionsBuilder) {
@ -31,7 +31,7 @@ export class StatusHistorySuggestionsSupplier {
return; return;
} }
const list = builder.getListAppender<StatusPanelOptions, StatusFieldConfig>({ const list = builder.getListAppender<PanelOptions, PanelFieldConfig>({
name: '', name: '',
pluginId: 'status-history', pluginId: 'status-history',
options: {}, options: {},

View File

@ -1,32 +0,0 @@
import {
HideableFieldConfig,
VisibilityMode,
OptionsWithTooltip,
OptionsWithLegend,
OptionsWithTimezones,
} from '@grafana/schema';
/**
* @alpha
*/
export interface StatusPanelOptions extends OptionsWithTooltip, OptionsWithLegend, OptionsWithTimezones {
showValue: VisibilityMode;
rowHeight: number;
colWidth?: number;
}
/**
* @alpha
*/
export interface StatusFieldConfig extends HideableFieldConfig {
lineWidth?: number; // 0
fillOpacity?: number; // 100
}
/**
* @alpha
*/
export const defaultStatusFieldConfig: StatusFieldConfig = {
lineWidth: 1,
fillOpacity: 70,
};