diff --git a/.betterer.results b/.betterer.results index 38db6f6dd98..390e7b0d98e 100644 --- a/.betterer.results +++ b/.betterer.results @@ -129,6 +129,9 @@ exports[`better eslint`] = { [0, 0, 0, "Do not use any type assertions.", "1"], [0, 0, 0, "Do not use any type assertions.", "2"] ], + "packages/grafana-data/src/field/fieldState.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], "packages/grafana-data/src/field/overrides/processors.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"], [0, 0, 0, "Unexpected any. Specify a different type.", "1"], @@ -5943,6 +5946,32 @@ exports[`better eslint`] = { [0, 0, 0, "Unexpected any. Specify a different type.", "9"], [0, 0, 0, "Do not use any type assertions.", "10"] ], + "public/app/plugins/panel/xychart/v2/SeriesEditor.tsx:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"], + [0, 0, 0, "Do not use any type assertions.", "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/xychart/v2/migrations.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"] + ], + "public/app/plugins/panel/xychart/v2/scatter.ts:5381": [ + [0, 0, 0, "Do not use any type assertions.", "0"], + [0, 0, 0, "Do not use any type assertions.", "1"], + [0, 0, 0, "Do not use any type assertions.", "2"], + [0, 0, 0, "Unexpected any. Specify a different type.", "3"], + [0, 0, 0, "Do not use any type assertions.", "4"], + [0, 0, 0, "Unexpected any. Specify a different type.", "5"], + [0, 0, 0, "Do not use any type assertions.", "6"], + [0, 0, 0, "Unexpected any. Specify a different type.", "7"], + [0, 0, 0, "Do not use any type assertions.", "8"], + [0, 0, 0, "Unexpected any. Specify a different type.", "9"] + ], + "public/app/plugins/panel/xychart/v2/utils.ts:5381": [ + [0, 0, 0, "Unexpected any. Specify a different type.", "0"] + ], "public/app/store/configureStore.ts:5381": [ [0, 0, 0, "Unexpected any. Specify a different type.", "0"] ], diff --git a/devenv/dev-dashboards/panel-xychart/xychart-example.json b/devenv/dev-dashboards/panel-xychart/xychart-example.json index cecdcb0e49d..61d9761864b 100644 --- a/devenv/dev-dashboards/panel-xychart/xychart-example.json +++ b/devenv/dev-dashboards/panel-xychart/xychart-example.json @@ -206,7 +206,7 @@ { "name": "Male", "pointColor": { - "fixed": "#5795f200" + "fixed": "#5795f2" }, "pointSize": { "fixed": 5, @@ -219,7 +219,7 @@ { "name": "Female", "pointColor": { - "fixed": "#ff983000" + "fixed": "#ff9830" }, "pointSize": { "fixed": 5, diff --git a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md index 35081c71992..1253cc1f979 100644 --- a/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md +++ b/docs/sources/setup-grafana/configure-grafana/feature-toggles/index.md @@ -72,6 +72,7 @@ Some features are enabled by default. You can disable these feature by setting t | `autoMigratePiechartPanel` | Migrate old piechart panel to supported piechart panel - broken out from autoMigrateOldPanels to enable granular tracking | | `autoMigrateWorldmapPanel` | Migrate old worldmap panel to supported geomap panel - broken out from autoMigrateOldPanels to enable granular tracking | | `autoMigrateStatPanel` | Migrate old stat panel to supported stat panel - broken out from autoMigrateOldPanels to enable granular tracking | +| `autoMigrateXYChartPanel` | Migrate old XYChart panel to new XYChart2 model | | `disableAngular` | Dynamic flag to disable angular at runtime. The preferred method is to set `angular_support_enabled` to `false` in the [security] settings, which allows you to change the state at runtime. | | `newVizTooltips` | New visualizations tooltips UX | | `returnToPrevious` | Enables the return to previous context functionality | diff --git a/packages/grafana-data/src/dataframe/index.ts b/packages/grafana-data/src/dataframe/index.ts index 9f35f56765d..3e14458a1f0 100644 --- a/packages/grafana-data/src/dataframe/index.ts +++ b/packages/grafana-data/src/dataframe/index.ts @@ -1,6 +1,5 @@ export * from './DataFrameView'; export * from './FieldCache'; -export * from './CircularDataFrame'; export * from './MutableDataFrame'; export * from './processDataFrame'; export * from './dimensions'; diff --git a/packages/grafana-data/src/field/fieldState.ts b/packages/grafana-data/src/field/fieldState.ts index 7ae0552baa2..065960148e0 100644 --- a/packages/grafana-data/src/field/fieldState.ts +++ b/packages/grafana-data/src/field/fieldState.ts @@ -1,4 +1,12 @@ -import { DataFrame, Field, TIME_SERIES_VALUE_FIELD_NAME, FieldType, TIME_SERIES_TIME_FIELD_NAME } from '../types'; +import { getFieldMatcher } from '../transformations'; +import { + DataFrame, + Field, + TIME_SERIES_VALUE_FIELD_NAME, + FieldType, + TIME_SERIES_TIME_FIELD_NAME, + FieldConfigSource, +} from '../types'; import { formatLabels } from '../utils/labels'; /** @@ -51,6 +59,53 @@ export function cacheFieldDisplayNames(frames: DataFrame[]) { }); } +/** + * + * moves each field's config.custom.hideFrom to field.state.hideFrom + * and mutates orgiginal field.config.custom.hideFrom to one with explicit overrides only, (without the ad-hoc stateful __system override from legend toggle) + */ +export function decoupleHideFromState(frames: DataFrame[], fieldConfig: FieldConfigSource) { + frames.forEach((frame) => { + frame.fields.forEach((field) => { + const hideFrom = { + legend: false, + tooltip: false, + viz: false, + ...fieldConfig.defaults.custom?.hideFrom, + }; + + // with ad hoc __system override applied + const hideFromState = field.config.custom?.hideFrom; + + fieldConfig.overrides.forEach((o) => { + if ('__systemRef' in o) { + return; + } + + const m = getFieldMatcher(o.matcher); + + if (m(field, frame, frames)) { + for (const p of o.properties) { + if (p.id === 'custom.hideFrom') { + Object.assign(hideFrom, p.value); + } + } + } + }); + + field.state = { + ...field.state, + hideFrom: { + ...hideFromState, + }, + }; + + // original with perm overrides + field.config.custom.hideFrom = hideFrom; + }); + }); +} + export function getFieldDisplayName(field: Field, frame?: DataFrame, allFrames?: DataFrame[]): string { const existingTitle = field.state?.displayName; const multipleFrames = Boolean(allFrames && allFrames.length > 1); diff --git a/packages/grafana-data/src/index.ts b/packages/grafana-data/src/index.ts index 5b14f5dc234..bbe35a8fbe1 100644 --- a/packages/grafana-data/src/index.ts +++ b/packages/grafana-data/src/index.ts @@ -48,3 +48,4 @@ export { getLinksSupplier } from './field/fieldOverrides'; export { CircularVector } from './vector/CircularVector'; export { vectorator } from './vector/FunctionalVector'; export { ArrayVector } from './vector/ArrayVector'; +export * from './dataframe/CircularDataFrame'; diff --git a/packages/grafana-data/src/types/dataFrame.ts b/packages/grafana-data/src/types/dataFrame.ts index 28b4dbadf25..291f88b2333 100644 --- a/packages/grafana-data/src/types/dataFrame.ts +++ b/packages/grafana-data/src/types/dataFrame.ts @@ -1,3 +1,5 @@ +import { HideSeriesConfig } from '@grafana/schema'; + import { ScopedVars } from './ScopedVars'; import { QueryResultBase, Labels, NullValueMode } from './data'; import { DataLink, LinkModel } from './dataLink'; @@ -231,6 +233,15 @@ export interface FieldState { * It's up to each visualization to calculate and set this. */ alignmentFactors?: DisplayValueAlignmentFactors; + + /** + * This is the current ad-hoc state of whether this series is hidden in viz, tooltip, and legend. + * + * Currently this will match field.config.custom.hideFrom because fieldOverrides applies the special __system + * override to the actual config during toggle via legend. This should go away once we have a unified system + * for layering ad hoc field overrides and options but still being able to get the stateless fieldConfig and panel options + */ + hideFrom?: HideSeriesConfig; } /** @public */ diff --git a/packages/grafana-data/src/types/featureToggles.gen.ts b/packages/grafana-data/src/types/featureToggles.gen.ts index 6aceeb5f066..85cdfedfeba 100644 --- a/packages/grafana-data/src/types/featureToggles.gen.ts +++ b/packages/grafana-data/src/types/featureToggles.gen.ts @@ -38,6 +38,7 @@ export interface FeatureToggles { autoMigratePiechartPanel?: boolean; autoMigrateWorldmapPanel?: boolean; autoMigrateStatPanel?: boolean; + autoMigrateXYChartPanel?: boolean; disableAngular?: boolean; canvasPanelNesting?: boolean; newVizTooltips?: boolean; diff --git a/packages/grafana-schema/src/raw/composable/xychart2/panelcfg/x/XYChartPanelCfg_types.gen.ts b/packages/grafana-schema/src/raw/composable/xychart2/panelcfg/x/XYChartPanelCfg_types.gen.ts new file mode 100644 index 00000000000..286f9026b4c --- /dev/null +++ b/packages/grafana-schema/src/raw/composable/xychart2/panelcfg/x/XYChartPanelCfg_types.gen.ts @@ -0,0 +1,95 @@ +// 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 pluginVersion = "11.0.0-pre"; + +/** + * Auto is "table" in the UI + */ +export enum SeriesMapping { + Auto = 'auto', + Manual = 'manual', +} + +export enum XYShowMode { + Lines = 'lines', + Points = 'points', + PointsAndLines = 'points+lines', +} + +/** + * NOTE: (copied from dashboard_kind.cue, since not exported) + * Matcher is a predicate configuration. Based on the config a set of field(s) or values is filtered in order to apply override / transformation. + * It comes with in id ( to resolve implementation from registry) and a configuration that’s specific to a particular matcher type. + */ +export interface MatcherConfig { + /** + * The matcher id. This is used to find the matcher implementation from registry. + */ + id: string; + /** + * The matcher options. This is specific to the matcher implementation. + */ + options?: unknown; +} + +export const defaultMatcherConfig: Partial = { + id: '', +}; + +export interface FieldConfig extends common.HideableFieldConfig, common.AxisConfig { + lineStyle?: common.LineStyle; + /** + * lineColor?: common.ColorDimensionConfig + */ + lineWidth?: number; + pointSize?: { + fixed?: number; + min?: number; + max?: number; + }; + show?: XYShowMode; +} + +export const defaultFieldConfig: Partial = { + show: XYShowMode.Points, +}; + +export interface XYSeriesConfig { + color?: { + matcher: MatcherConfig; + }; + frame?: { + matcher: MatcherConfig; + }; + name?: { + fixed?: string; + }; + size?: { + matcher: MatcherConfig; + }; + x?: { + matcher: MatcherConfig; + }; + y?: { + matcher: MatcherConfig; + }; +} + +export interface Options extends common.OptionsWithLegend, common.OptionsWithTooltip { + mapping: SeriesMapping; + series: Array; +} + +export const defaultOptions: Partial = { + series: [], +}; diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx index 140e5c3df37..8aad8d1decc 100644 --- a/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegend.tsx @@ -72,7 +72,7 @@ export function VizLegend({ } if (onToggleSeriesVisibility) { onToggleSeriesVisibility( - item.label, + item.fieldName ?? item.label, seriesVisibilityChangeBehavior === SeriesVisibilityChangeBehavior.Hide ? SeriesVisibilityChangeMode.AppendToSelection : mapMouseEventToMode(event) diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx index 0518ae5ac5d..5a43727e728 100644 --- a/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendListItem.tsx @@ -70,7 +70,12 @@ export const VizLegendListItem = ({ className={cx(styles.itemWrapper, item.disabled && styles.itemDisabled, className)} data-testid={selectors.components.VizLegend.seriesName(item.label)} > - + + +
+ {seriesCfg.map((series, index) => { + return ( +
setSelectedIdx(index)} + role="button" + aria-label={`Select series ${index + 1}`} + tabIndex={0} + onKeyPress={(e) => { + if (e.key === 'Enter') { + setSelectedIdx(index); + } + }} + > + { + series.name = { + fixed: v === '' || v === `Series ${index + 1}` ? undefined : v, + }; + onChange([...seriesCfg]); + }} + /> + deleteSeries(index)} + tooltip="Delete series" + /> +
+ ); + })} +
+ + )} + + + +