From 6a93c7708235a11b5df49566bb72ab1dbe0e1ec1 Mon Sep 17 00:00:00 2001 From: Victor Marin <36818606+mdvictor@users.noreply.github.com> Date: Thu, 26 Jan 2023 09:03:59 +0200 Subject: [PATCH] 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 --- .betterer.results | 38 +++++------ .../statetimelinepanelcfg/schema-reference.md | 41 ++++++++++++ .../statushistorypanelcfg/schema-reference.md | 39 +++++++++++ .../grafana-schema/src/common/common.gen.ts | 5 ++ .../grafana-schema/src/common/mudball.cue | 3 + pkg/kindsys/report.json | 56 +++++++++++++++- pkg/plugins/pfs/corelist/corelist_load_gen.go | 2 + .../TimelineChart}/TimelineChart.tsx | 11 ++-- .../components/TimelineChart}/timeline.ts | 12 ++-- .../components/TimelineChart}/utils.test.ts | 0 .../components/TimelineChart}/utils.ts | 51 +++++++++++--- public/app/plugins/gen.go | 18 +++-- .../plugins/panel/barchart/BarChartPanel.tsx | 2 +- .../panel/geomap/components/MarkersLegend.tsx | 2 +- .../state-timeline/StateTimelinePanel.tsx | 12 ++-- .../state-timeline/StateTimelineTooltip.tsx | 3 +- .../panel/state-timeline/migrations.ts | 8 +-- .../plugins/panel/state-timeline/module.tsx | 8 +-- .../plugins/panel/state-timeline/panelcfg.cue | 31 ++++----- .../panel/state-timeline/panelcfg.gen.ts | 49 ++++++++++++++ .../panel/state-timeline/suggestions.ts | 4 +- .../app/plugins/panel/state-timeline/types.ts | 66 ------------------- .../status-history/StatusHistoryPanel.tsx | 14 ++-- .../plugins/panel/status-history/module.tsx | 18 ++--- .../plugins/panel/status-history/panelcfg.cue | 24 +++---- .../panel/status-history/panelcfg.gen.ts | 39 +++++++++++ .../panel/status-history/suggestions.ts | 4 +- .../app/plugins/panel/status-history/types.ts | 32 --------- 28 files changed, 376 insertions(+), 216 deletions(-) create mode 100644 docs/sources/developers/kinds/composable/statetimelinepanelcfg/schema-reference.md create mode 100644 docs/sources/developers/kinds/composable/statushistorypanelcfg/schema-reference.md rename public/app/{plugins/panel/state-timeline => core/components/TimelineChart}/TimelineChart.tsx (88%) rename public/app/{plugins/panel/state-timeline => core/components/TimelineChart}/timeline.ts (96%) rename public/app/{plugins/panel/state-timeline => core/components/TimelineChart}/utils.test.ts (100%) rename public/app/{plugins/panel/state-timeline => core/components/TimelineChart}/utils.ts (94%) create mode 100644 public/app/plugins/panel/state-timeline/panelcfg.gen.ts delete mode 100644 public/app/plugins/panel/state-timeline/types.ts create mode 100644 public/app/plugins/panel/status-history/panelcfg.gen.ts delete mode 100644 public/app/plugins/panel/status-history/types.ts diff --git a/.betterer.results b/.betterer.results index fcb25c5f876..05de8a35f8a 100644 --- a/.betterer.results +++ b/.betterer.results @@ -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.", "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": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [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, "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": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [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.", "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": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], diff --git a/docs/sources/developers/kinds/composable/statetimelinepanelcfg/schema-reference.md b/docs/sources/developers/kinds/composable/statetimelinepanelcfg/schema-reference.md new file mode 100644 index 00000000000..326d99d4a84 --- /dev/null +++ b/docs/sources/developers/kinds/composable/statetimelinepanelcfg/schema-reference.md @@ -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`. | + + diff --git a/docs/sources/developers/kinds/composable/statushistorypanelcfg/schema-reference.md b/docs/sources/developers/kinds/composable/statushistorypanelcfg/schema-reference.md new file mode 100644 index 00000000000..2daba7d77d2 --- /dev/null +++ b/docs/sources/developers/kinds/composable/statushistorypanelcfg/schema-reference.md @@ -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`. | + + diff --git a/packages/grafana-schema/src/common/common.gen.ts b/packages/grafana-schema/src/common/common.gen.ts index b64a54dcc30..b4bf0b99b55 100644 --- a/packages/grafana-schema/src/common/common.gen.ts +++ b/packages/grafana-schema/src/common/common.gen.ts @@ -485,6 +485,11 @@ export enum BigValueTextMode { */ export type FieldTextAlignment = ('auto' | 'left' | 'right' | 'center'); +/** + * Controls the value alignment in the TimelineChart component + */ +export type TimelineValueAlignment = ('center' | 'left' | 'right'); + /** * TODO docs */ diff --git a/packages/grafana-schema/src/common/mudball.cue b/packages/grafana-schema/src/common/mudball.cue index 537c6549570..bdb88a689e5 100644 --- a/packages/grafana-schema/src/common/mudball.cue +++ b/packages/grafana-schema/src/common/mudball.cue @@ -193,6 +193,9 @@ BigValueTextMode: "auto" | "value" | "value_and_name" | "name" | "none" @cuetsy( // TODO docs 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 VizTextDisplayOptions: { // Explicit title text size diff --git a/pkg/kindsys/report.json b/pkg/kindsys/report.json index 55cecf715c2..f47f85e3a0d 100644 --- a/pkg/kindsys/report.json +++ b/pkg/kindsys/report.json @@ -1349,6 +1349,30 @@ "pluralMachineName": "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": { "category": "composable", "codeowners": [ @@ -1373,6 +1397,30 @@ "pluralName": "StatPanelCfgs", "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": { "category": "composable", "codeowners": [], @@ -1744,7 +1792,9 @@ "postgresqldatasourcecfg", "prometheusdataquery", "prometheusdatasourcecfg", + "statetimelinepanelcfg", "statpanelcfg", + "statushistorypanelcfg", "tableoldpanelcfg", "tempodataquery", "tempodatasourcecfg", @@ -1757,7 +1807,7 @@ "zipkindataquery", "zipkindatasourcecfg" ], - "count": 63 + "count": 65 }, "core": { "name": "core", @@ -1791,11 +1841,13 @@ "histogrampanelcfg", "newspanelcfg", "piechartpanelcfg", + "statetimelinepanelcfg", "statpanelcfg", + "statushistorypanelcfg", "textpanelcfg", "xychartpanelcfg" ], - "count": 12 + "count": 14 }, "mature": { "name": "mature", diff --git a/pkg/plugins/pfs/corelist/corelist_load_gen.go b/pkg/plugins/pfs/corelist/corelist_load_gen.go index 77b8a15304c..4ba87ce393d 100644 --- a/pkg/plugins/pfs/corelist/corelist_load_gen.go +++ b/pkg/plugins/pfs/corelist/corelist_load_gen.go @@ -71,6 +71,8 @@ func corePlugins(rt *thema.Runtime) []pfs.ParsedPlugin { parsePluginOrPanic("public/app/plugins/panel/nodeGraph", "nodeGraph", rt), parsePluginOrPanic("public/app/plugins/panel/piechart", "piechart", 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/text", "text", rt), parsePluginOrPanic("public/app/plugins/panel/traces", "traces", rt), diff --git a/public/app/plugins/panel/state-timeline/TimelineChart.tsx b/public/app/core/components/TimelineChart/TimelineChart.tsx similarity index 88% rename from public/app/plugins/panel/state-timeline/TimelineChart.tsx rename to public/app/core/components/TimelineChart/TimelineChart.tsx index cd853ded8ac..9126adaea70 100644 --- a/public/app/plugins/panel/state-timeline/TimelineChart.tsx +++ b/public/app/core/components/TimelineChart/TimelineChart.tsx @@ -1,7 +1,7 @@ import React from 'react'; import { DataFrame, FALLBACK_COLOR, FieldType, TimeRange } from '@grafana/data'; -import { VisibilityMode } from '@grafana/schema'; +import { VisibilityMode, TimelineValueAlignment } from '@grafana/schema'; import { PanelContext, PanelContextRoot, @@ -13,17 +13,14 @@ import { VizLegendItem, } from '@grafana/ui'; -import { TimelineMode, TimelineOptions, TimelineValueAlignment } from './types'; -import { preparePlotConfigBuilder } from './utils'; +import { preparePlotConfigBuilder, TimelineMode } from './utils'; /** * @alpha */ -export interface TimelineProps - extends TimelineOptions, - Omit { +export interface TimelineProps extends Omit { mode: TimelineMode; - rowHeight: number; + rowHeight?: number; showValue: VisibilityMode; alignValue?: TimelineValueAlignment; colWidth?: number; diff --git a/public/app/plugins/panel/state-timeline/timeline.ts b/public/app/core/components/TimelineChart/timeline.ts similarity index 96% rename from public/app/plugins/panel/state-timeline/timeline.ts rename to public/app/core/components/TimelineChart/timeline.ts index eeb0c634fdf..e6f968fcd39 100644 --- a/public/app/plugins/panel/state-timeline/timeline.ts +++ b/public/app/core/components/TimelineChart/timeline.ts @@ -2,12 +2,14 @@ import uPlot, { Cursor, Series } from 'uplot'; import { GrafanaTheme2, TimeRange } from '@grafana/data'; 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 { distribute, SPACE_BETWEEN } from 'app/plugins/panel/barchart/distribute'; 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; @@ -39,7 +41,7 @@ export interface TimelineCoreOptions { mode: TimelineMode; alignValue?: TimelineValueAlignment; numSeries: number; - rowHeight: number; + rowHeight?: number; colWidth?: number; theme: GrafanaTheme2; showValue: VisibilityMode; @@ -49,7 +51,7 @@ export interface TimelineCoreOptions { label: (seriesIdx: number) => string; getTimeRange: () => TimeRange; formatValue?: (seriesIdx: number, value: any) => string; - getFieldConfig: (seriesIdx: number) => TimelineFieldConfig; + getFieldConfig: (seriesIdx: number) => StateTimeLineFieldConfig | StatusHistoryFieldConfig; onHover: (seriesIdx: number, valueIdx: number, rect: Rect) => 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 // e.g. thresholds with opacity if (color[0] === '#' && color.length === 9) { diff --git a/public/app/plugins/panel/state-timeline/utils.test.ts b/public/app/core/components/TimelineChart/utils.test.ts similarity index 100% rename from public/app/plugins/panel/state-timeline/utils.test.ts rename to public/app/core/components/TimelineChart/utils.test.ts diff --git a/public/app/plugins/panel/state-timeline/utils.ts b/public/app/core/components/TimelineChart/utils.ts similarity index 94% rename from public/app/plugins/panel/state-timeline/utils.ts rename to public/app/core/components/TimelineChart/utils.ts index ba2b7db6a8c..7351ca00f00 100644 --- a/public/app/plugins/panel/state-timeline/utils.ts +++ b/public/app/core/components/TimelineChart/utils.ts @@ -24,7 +24,15 @@ import { TimeRange, } from '@grafana/data'; 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 { FIXED_UNIT, 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 { 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, fillOpacity: 80, }; @@ -52,7 +88,7 @@ export function mapMouseEventToMode(event: React.MouseEvent): SeriesVisibilityCh return SeriesVisibilityChangeMode.ToggleSelection; } -export const preparePlotConfigBuilder: UPlotConfigPrepFn = ({ +export const preparePlotConfigBuilder: UPlotConfigPrepFn = ({ frame, theme, timeZones, @@ -92,12 +128,11 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn = ({ }; const opts: TimelineCoreOptions = { - // should expose in panel config mode: mode!, numSeries: frame.fields.length - 1, isDiscrete: (seriesIdx) => isDiscrete(frame.fields[seriesIdx]), mergeValues, - rowHeight: rowHeight!, + rowHeight: rowHeight, colWidth: colWidth, showValue: showValue!, alignValue, @@ -209,8 +244,8 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn = ({ } const field = frame.fields[i]; - const config: FieldConfig = field.config; - const customConfig: TimelineFieldConfig = { + const config: FieldConfig = field.config; + const customConfig: PanelFieldConfig = { ...defaultConfig, ...config.custom, }; diff --git a/public/app/plugins/gen.go b/public/app/plugins/gen.go index f79e6d6c869..c0d169e033d 100644 --- a/public/app/plugins/gen.go +++ b/public/app/plugins/gen.go @@ -21,16 +21,14 @@ import ( ) var skipPlugins = map[string]bool{ - "canvas": true, - "heatmap": true, - "candlestick": true, - "state-timeline": true, - "status-history": true, - "table": true, - "timeseries": true, - "influxdb": true, // plugin.json fails validation (defaultMatchFormat) - "mixed": true, // plugin.json fails validation (mixed) - "opentsdb": true, // plugin.json fails validation (defaultMatchFormat) + "canvas": true, + "heatmap": true, + "candlestick": true, + "table": true, + "timeseries": true, + "influxdb": 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) diff --git a/public/app/plugins/panel/barchart/BarChartPanel.tsx b/public/app/plugins/panel/barchart/BarChartPanel.tsx index d1a680bae88..7ac09a8d4a8 100644 --- a/public/app/plugins/panel/barchart/BarChartPanel.tsx +++ b/public/app/plugins/panel/barchart/BarChartPanel.tsx @@ -33,9 +33,9 @@ import { import { PropDiffFn } from '@grafana/ui/src/components/GraphNG/GraphNG'; import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport'; import { CloseButton } from 'app/core/components/CloseButton/CloseButton'; +import { getFieldLegendItem } from 'app/core/components/TimelineChart/utils'; import { DataHoverView } from '../geomap/components/DataHoverView'; -import { getFieldLegendItem } from '../state-timeline/utils'; import { PanelOptions } from './panelcfg.gen'; import { prepareBarChartDisplayValues, preparePlotConfigBuilder } from './utils'; diff --git a/public/app/plugins/panel/geomap/components/MarkersLegend.tsx b/public/app/plugins/panel/geomap/components/MarkersLegend.tsx index 2833351acf2..f9e0c75ccf8 100644 --- a/public/app/plugins/panel/geomap/components/MarkersLegend.tsx +++ b/public/app/plugins/panel/geomap/components/MarkersLegend.tsx @@ -9,9 +9,9 @@ import { getMinMaxAndDelta } from '@grafana/data/src/field/scale'; import { useStyles2, VizLegendItem } from '@grafana/ui'; import { ColorScale } from 'app/core/components/ColorScale/ColorScale'; import { SanitizedSVG } from 'app/core/components/SVG/SanitizedSVG'; +import { getThresholdItems } from 'app/core/components/TimelineChart/utils'; import { config } from 'app/core/config'; import { DimensionSupplier } from 'app/features/dimensions'; -import { getThresholdItems } from 'app/plugins/panel/state-timeline/utils'; import { StyleConfigState } from '../style/types'; import { MapLayerState } from '../types'; diff --git a/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx index daaa2750bdc..66e5ca76c74 100644 --- a/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx +++ b/public/app/plugins/panel/state-timeline/StateTimelinePanel.tsx @@ -12,6 +12,12 @@ import { } from '@grafana/ui'; import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport'; 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 { AnnotationEditorPlugin } from '../timeseries/plugins/AnnotationEditorPlugin'; @@ -20,13 +26,11 @@ import { OutsideRangePlugin } from '../timeseries/plugins/OutsideRangePlugin'; import { getTimezones } from '../timeseries/utils'; import { StateTimelineTooltip } from './StateTimelineTooltip'; -import { TimelineChart } from './TimelineChart'; -import { TimelineMode, TimelineOptions } from './types'; -import { prepareTimelineFields, prepareTimelineLegendItems } from './utils'; +import { PanelOptions } from './panelcfg.gen'; const TOOLTIP_OFFSET = 10; -interface TimelinePanelProps extends PanelProps {} +interface TimelinePanelProps extends PanelProps {} /** * @alpha diff --git a/public/app/plugins/panel/state-timeline/StateTimelineTooltip.tsx b/public/app/plugins/panel/state-timeline/StateTimelineTooltip.tsx index 65047f08224..0e4e7c7808d 100644 --- a/public/app/plugins/panel/state-timeline/StateTimelineTooltip.tsx +++ b/public/app/plugins/panel/state-timeline/StateTimelineTooltip.tsx @@ -10,8 +10,7 @@ import { LinkModel, } from '@grafana/data'; import { MenuItem, SeriesTableRow, useTheme2 } from '@grafana/ui'; - -import { findNextStateIndex, fmtDuration } from './utils'; +import { findNextStateIndex, fmtDuration } from 'app/core/components/TimelineChart/utils'; interface StateTimelineTooltipProps { data: DataFrame[]; diff --git a/public/app/plugins/panel/state-timeline/migrations.ts b/public/app/plugins/panel/state-timeline/migrations.ts index 35707074624..9e09969706a 100644 --- a/public/app/plugins/panel/state-timeline/migrations.ts +++ b/public/app/plugins/panel/state-timeline/migrations.ts @@ -2,15 +2,15 @@ import { isArray } from 'lodash'; 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 export const timelinePanelChangedHandler = ( - panel: PanelModel> | any, + panel: PanelModel> | any, prevPluginId: string, prevOptions: any ) => { - let options = (panel.options ?? {}) as TimelineOptions; + let options = (panel.options ?? {}) as PanelOptions; // Changing from angular singlestat if (prevPluginId === 'natel-discrete-panel' && prevOptions.angular) { @@ -21,7 +21,7 @@ export const timelinePanelChangedHandler = ( fieldConfig.defaults.unit = oldOptions.units; } - const custom: TimelineFieldConfig = { + const custom: PanelFieldConfig = { fillOpacity: 100, lineWidth: 0, }; diff --git a/public/app/plugins/panel/state-timeline/module.tsx b/public/app/plugins/panel/state-timeline/module.tsx index 5fdd2ab1bbb..7782fc6f984 100644 --- a/public/app/plugins/panel/state-timeline/module.tsx +++ b/public/app/plugins/panel/state-timeline/module.tsx @@ -12,10 +12,10 @@ import { SpanNullsEditor } from '../timeseries/SpanNullsEditor'; import { StateTimelinePanel } from './StateTimelinePanel'; import { timelinePanelChangedHandler } from './migrations'; +import { PanelOptions, PanelFieldConfig, defaultPanelOptions, defaultPanelFieldConfig } from './panelcfg.gen'; import { StatTimelineSuggestionsSupplier } from './suggestions'; -import { TimelineOptions, TimelineFieldConfig, defaultPanelOptions, defaultTimelineFieldConfig } from './types'; -export const plugin = new PanelPlugin(StateTimelinePanel) +export const plugin = new PanelPlugin(StateTimelinePanel) .setPanelChangeHandler(timelinePanelChangedHandler) .useFieldConfig({ standardOptions: { @@ -33,7 +33,7 @@ export const plugin = new PanelPlugin(Stat .addSliderInput({ path: 'lineWidth', name: 'Line width', - defaultValue: defaultTimelineFieldConfig.lineWidth, + defaultValue: defaultPanelFieldConfig.lineWidth, settings: { min: 0, max: 10, @@ -43,7 +43,7 @@ export const plugin = new PanelPlugin(Stat .addSliderInput({ path: 'fillOpacity', name: 'Fill opacity', - defaultValue: defaultTimelineFieldConfig.fillOpacity, + defaultValue: defaultPanelFieldConfig.fillOpacity, settings: { min: 0, max: 100, diff --git a/public/app/plugins/panel/state-timeline/panelcfg.cue b/public/app/plugins/panel/state-timeline/panelcfg.cue index dd0a822f463..dc35ccf0fdb 100644 --- a/public/app/plugins/panel/state-timeline/panelcfg.cue +++ b/public/app/plugins/panel/state-timeline/panelcfg.cue @@ -15,33 +15,34 @@ package grafanaplugin import ( - "github.com/grafana/grafana/packages/grafana-schema/src/common" + ui "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { + maturity: "experimental" lineage: { seqs: [ { schemas: [ { - TimelineMode: "changes" | "samples" @cuetsy(kind="enum") - TimelineValueAlignment: "center" | "left" | "right" @cuetsy(kind="type") PanelOptions: { - // FIXME ts comments indicate this shouldn't be in the saved model, but currently is emitted - mode?: TimelineMode - common.OptionsWithLegend - common.OptionsWithTooltip - common.OptionsWithTimezones - showValue: common.VisibilityMode | *"auto" - rowHeight: number | *0.9 - colWidth?: number + ui.OptionsWithLegend + ui.OptionsWithTooltip + ui.OptionsWithTimezones + + //Show timeline values on chart + showValue: ui.VisibilityMode | *"auto" + //Controls the row height + rowHeight: float & <=1 | *0.9 + //Merge equal consecutive values mergeValues?: bool | *true - alignValue?: TimelineValueAlignment | *"left" + //Controls value alignment on the timelines + alignValue?: ui.TimelineValueAlignment | *"left" } @cuetsy(kind="interface") PanelFieldConfig: { - common.HideableFieldConfig - lineWidth?: number | *0 - fillOpacity?: number | *70 + ui.HideableFieldConfig + lineWidth?: uint32 & <=10 | *0 + fillOpacity?: uint32 & <=100 | *70 } @cuetsy(kind="interface") }, ] diff --git a/public/app/plugins/panel/state-timeline/panelcfg.gen.ts b/public/app/plugins/panel/state-timeline/panelcfg.gen.ts new file mode 100644 index 00000000000..f61ac232654 --- /dev/null +++ b/public/app/plugins/panel/state-timeline/panelcfg.gen.ts @@ -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 = { + 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 = { + fillOpacity: 70, + lineWidth: 0, +}; diff --git a/public/app/plugins/panel/state-timeline/suggestions.ts b/public/app/plugins/panel/state-timeline/suggestions.ts index 31ec82210a2..4e778f2ad65 100644 --- a/public/app/plugins/panel/state-timeline/suggestions.ts +++ b/public/app/plugins/panel/state-timeline/suggestions.ts @@ -1,7 +1,7 @@ import { VisualizationSuggestionsBuilder } from '@grafana/data'; import { SuggestionName } from 'app/types/suggestions'; -import { TimelineFieldConfig, TimelineOptions } from './types'; +import { PanelFieldConfig, PanelOptions } from './panelcfg.gen'; export class StatTimelineSuggestionsSupplier { getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { @@ -26,7 +26,7 @@ export class StatTimelineSuggestionsSupplier { return; } - const list = builder.getListAppender({ + const list = builder.getListAppender({ name: '', pluginId: 'state-timeline', options: {}, diff --git a/public/app/plugins/panel/state-timeline/types.ts b/public/app/plugins/panel/state-timeline/types.ts deleted file mode 100644 index fed8714bb21..00000000000 --- a/public/app/plugins/panel/state-timeline/types.ts +++ /dev/null @@ -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 = { - 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', -} diff --git a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx index 2aeb84b74b3..3c15376c4e6 100644 --- a/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx +++ b/public/app/plugins/panel/status-history/StatusHistoryPanel.tsx @@ -12,19 +12,22 @@ import { } from '@grafana/ui'; import { HoverEvent, addTooltipSupport } from '@grafana/ui/src/components/uPlot/config/addTooltipSupport'; 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 { getTimezones } from '../timeseries/utils'; import { StatusHistoryTooltip } from './StatusHistoryTooltip'; -import { StatusPanelOptions } from './types'; +import { PanelOptions } from './panelcfg.gen'; const TOOLTIP_OFFSET = 10; -interface TimelinePanelProps extends PanelProps {} +interface TimelinePanelProps extends PanelProps {} /** * @alpha @@ -193,7 +196,6 @@ export const StatusHistoryPanel: React.FC = ({ height={height} legendItems={legendItems} {...options} - // hardcoded mode={TimelineMode.Samples} > {(config, alignedFrame) => { diff --git a/public/app/plugins/panel/status-history/module.tsx b/public/app/plugins/panel/status-history/module.tsx index 1f961295ce6..5f7a1ec915e 100644 --- a/public/app/plugins/panel/status-history/module.tsx +++ b/public/app/plugins/panel/status-history/module.tsx @@ -3,10 +3,10 @@ import { VisibilityMode } from '@grafana/schema'; import { commonOptionsBuilder } from '@grafana/ui'; import { StatusHistoryPanel } from './StatusHistoryPanel'; +import { PanelOptions, PanelFieldConfig, defaultPanelFieldConfig } from './panelcfg.gen'; import { StatusHistorySuggestionsSupplier } from './suggestions'; -import { StatusPanelOptions, StatusFieldConfig, defaultStatusFieldConfig } from './types'; -export const plugin = new PanelPlugin(StatusHistoryPanel) +export const plugin = new PanelPlugin(StatusHistoryPanel) .useFieldConfig({ standardOptions: { [FieldConfigProperty.Color]: { @@ -23,7 +23,7 @@ export const plugin = new PanelPlugin(Sta .addSliderInput({ path: 'lineWidth', name: 'Line width', - defaultValue: defaultStatusFieldConfig.lineWidth, + defaultValue: defaultPanelFieldConfig.lineWidth, settings: { min: 0, max: 10, @@ -33,7 +33,7 @@ export const plugin = new PanelPlugin(Sta .addSliderInput({ path: 'fillOpacity', name: 'Fill opacity', - defaultValue: defaultStatusFieldConfig.fillOpacity, + defaultValue: defaultPanelFieldConfig.fillOpacity, settings: { min: 0, max: 100, @@ -56,16 +56,6 @@ export const plugin = new PanelPlugin(Sta }, defaultValue: VisibilityMode.Auto, }) - .addSliderInput({ - path: 'rowHeight', - name: 'Row height', - defaultValue: 0.9, - settings: { - min: 0, - max: 1, - step: 0.01, - }, - }) .addSliderInput({ path: 'colWidth', name: 'Column width', diff --git a/public/app/plugins/panel/status-history/panelcfg.cue b/public/app/plugins/panel/status-history/panelcfg.cue index b16499ad837..7dda14c56f4 100644 --- a/public/app/plugins/panel/status-history/panelcfg.cue +++ b/public/app/plugins/panel/status-history/panelcfg.cue @@ -15,28 +15,30 @@ package grafanaplugin import ( - "github.com/grafana/grafana/packages/grafana-schema/src/common" + ui "github.com/grafana/grafana/packages/grafana-schema/src/common" ) composableKinds: PanelCfg: { + maturity: "experimental" lineage: { seqs: [ { schemas: [ { PanelOptions: { - common.OptionsWithLegend - common.OptionsWithTooltip - common.OptionsWithTimezones - showValue: common.VisibilityMode - rowHeight: number - colWidth?: number - alignValue: "center" | *"left" | "right" + ui.OptionsWithLegend + ui.OptionsWithTooltip + ui.OptionsWithTimezones + + //Show values on the columns + showValue: ui.VisibilityMode | *"auto" + //Controls the column width + colWidth?: float & <=1 | *0.9 } @cuetsy(kind="interface") PanelFieldConfig: { - common.HideableFieldConfig - lineWidth?: number | *1 - fillOpacity?: number | *70 + ui.HideableFieldConfig + lineWidth?: uint32 & <=10 | *1 + fillOpacity?: uint32 & <=100 | *70 } @cuetsy(kind="interface") }, ] diff --git a/public/app/plugins/panel/status-history/panelcfg.gen.ts b/public/app/plugins/panel/status-history/panelcfg.gen.ts new file mode 100644 index 00000000000..49948fe6c3b --- /dev/null +++ b/public/app/plugins/panel/status-history/panelcfg.gen.ts @@ -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 = { + colWidth: 0.9, + showValue: ui.VisibilityMode.Auto, +}; + +export interface PanelFieldConfig extends ui.HideableFieldConfig { + fillOpacity?: number; + lineWidth?: number; +} + +export const defaultPanelFieldConfig: Partial = { + fillOpacity: 70, + lineWidth: 1, +}; diff --git a/public/app/plugins/panel/status-history/suggestions.ts b/public/app/plugins/panel/status-history/suggestions.ts index 37745263c81..5ca72215598 100644 --- a/public/app/plugins/panel/status-history/suggestions.ts +++ b/public/app/plugins/panel/status-history/suggestions.ts @@ -1,7 +1,7 @@ import { FieldColorModeId, VisualizationSuggestionsBuilder } from '@grafana/data'; import { SuggestionName } from 'app/types/suggestions'; -import { StatusPanelOptions, StatusFieldConfig } from './types'; +import { PanelOptions, PanelFieldConfig } from './panelcfg.gen'; export class StatusHistorySuggestionsSupplier { getSuggestionsForData(builder: VisualizationSuggestionsBuilder) { @@ -31,7 +31,7 @@ export class StatusHistorySuggestionsSupplier { return; } - const list = builder.getListAppender({ + const list = builder.getListAppender({ name: '', pluginId: 'status-history', options: {}, diff --git a/public/app/plugins/panel/status-history/types.ts b/public/app/plugins/panel/status-history/types.ts deleted file mode 100644 index 874d1a99def..00000000000 --- a/public/app/plugins/panel/status-history/types.ts +++ /dev/null @@ -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, -};