diff --git a/packages/grafana-ui/src/components/BarChart/utils.ts b/packages/grafana-ui/src/components/BarChart/utils.ts index 9983f60435f..5ae658b8d7a 100644 --- a/packages/grafana-ui/src/components/BarChart/utils.ts +++ b/packages/grafana-ui/src/components/BarChart/utils.ts @@ -117,7 +117,7 @@ export function preparePlotConfigBuilder( colorMode, pathBuilder: config.drawBars, pointsBuilder: config.drawPoints, - show: !customConfig.hideFrom?.graph, + show: !customConfig.hideFrom?.viz, gradientMode: customConfig.gradientMode, thresholds: field.config.thresholds, diff --git a/packages/grafana-ui/src/components/GraphNG/SeriesVisibilityChangeMode.ts b/packages/grafana-ui/src/components/GraphNG/SeriesVisibilityChangeMode.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/packages/grafana-ui/src/components/GraphNG/types.ts b/packages/grafana-ui/src/components/GraphNG/types.ts index d1f60a36c0e..a65aa037647 100644 --- a/packages/grafana-ui/src/components/GraphNG/types.ts +++ b/packages/grafana-ui/src/components/GraphNG/types.ts @@ -1,14 +1,5 @@ import { DataFrameFieldIndex, FieldMatcher } from '@grafana/data'; - -/** - * Mode to describe if a legend is isolated/selected or being appended to an existing - * series selection. - * @alpha - */ -export enum GraphNGLegendEventMode { - ToggleSelection = 'select', - AppendToSelection = 'append', -} +import { SeriesVisibilityChangeMode } from '..'; /** * Event being triggered when the user interact with the Graph legend. @@ -16,7 +7,7 @@ export enum GraphNGLegendEventMode { */ export interface GraphNGLegendEvent { fieldIndex: DataFrameFieldIndex; - mode: GraphNGLegendEventMode; + mode: SeriesVisibilityChangeMode; } /** @alpha */ diff --git a/packages/grafana-ui/src/components/GraphNG/utils.ts b/packages/grafana-ui/src/components/GraphNG/utils.ts index b85614d6f14..d01b8309432 100644 --- a/packages/grafana-ui/src/components/GraphNG/utils.ts +++ b/packages/grafana-ui/src/components/GraphNG/utils.ts @@ -1,5 +1,4 @@ -import React from 'react'; -import { GraphNGLegendEventMode, XYFieldMatchers } from './types'; +import { XYFieldMatchers } from './types'; import { ArrayVector, DataFrame, @@ -18,13 +17,6 @@ export interface PrepConfigOpts { [prop: string]: any; } -export function mapMouseEventToMode(event: React.MouseEvent): GraphNGLegendEventMode { - if (event.ctrlKey || event.metaKey || event.shiftKey) { - return GraphNGLegendEventMode.AppendToSelection; - } - return GraphNGLegendEventMode.ToggleSelection; -} - function applySpanNullsThresholds(frames: DataFrame[]) { for (const frame of frames) { let refField = frame.fields.find((field) => field.type === FieldType.time); // this doesnt need to be time, just any numeric/asc join field diff --git a/packages/grafana-ui/src/components/PanelChrome/PanelContext.ts b/packages/grafana-ui/src/components/PanelChrome/PanelContext.ts index a019f85e6bc..835c790442e 100644 --- a/packages/grafana-ui/src/components/PanelChrome/PanelContext.ts +++ b/packages/grafana-ui/src/components/PanelChrome/PanelContext.ts @@ -1,5 +1,6 @@ import { EventBusSrv, EventBus } from '@grafana/data'; import React from 'react'; +import { SeriesVisibilityChangeMode } from '.'; /** @alpha */ export interface PanelContext { @@ -11,6 +12,8 @@ export interface PanelContext { * @alpha -- experimental */ onSeriesColorChange?: (label: string, color: string) => void; + + onToggleSeriesVisibility?: (label: string, mode: SeriesVisibilityChangeMode) => void; } const PanelContextRoot = React.createContext({ diff --git a/packages/grafana-ui/src/components/PanelChrome/index.ts b/packages/grafana-ui/src/components/PanelChrome/index.ts index ca06b467a8c..69889b72b37 100644 --- a/packages/grafana-ui/src/components/PanelChrome/index.ts +++ b/packages/grafana-ui/src/components/PanelChrome/index.ts @@ -38,3 +38,5 @@ export { } from './ErrorIndicator'; export { usePanelContext, PanelContextProvider, PanelContext } from './PanelContext'; + +export * from './types'; diff --git a/packages/grafana-ui/src/components/PanelChrome/types.ts b/packages/grafana-ui/src/components/PanelChrome/types.ts new file mode 100644 index 00000000000..0d7e74cf781 --- /dev/null +++ b/packages/grafana-ui/src/components/PanelChrome/types.ts @@ -0,0 +1,10 @@ +/** + * Mode to describe if a legend is isolated/selected or being appended to an existing + * series selection. + * @alpha + */ + +export enum SeriesVisibilityChangeMode { + ToggleSelection = 'select', + AppendToSelection = 'append', +} diff --git a/packages/grafana-ui/src/components/PieChart/PieChart.tsx b/packages/grafana-ui/src/components/PieChart/PieChart.tsx index 3aa8934a884..4c2bc2dd8d6 100644 --- a/packages/grafana-ui/src/components/PieChart/PieChart.tsx +++ b/packages/grafana-ui/src/components/PieChart/PieChart.tsx @@ -35,6 +35,7 @@ import { getTooltipContainerStyles } from '../../themes/mixins'; import { SeriesTable, SeriesTableRowProps, VizTooltipOptions } from '../VizTooltip'; import { usePanelContext } from '../PanelChrome'; import { Subscription } from 'rxjs'; +import { SeriesVisibilityChangeBehavior } from '../VizLegend/types'; const defaultLegendOptions: PieChartLegendOptions = { displayMode: LegendDisplayMode.List, @@ -94,31 +95,37 @@ function getLegend(props: PieChartProps, displayValues: FieldDisplay[]) { if (legendOptions.displayMode === LegendDisplayMode.Hidden) { return undefined; } - const values = displayValues.map((v) => v.display); - const total = values.reduce((acc, item) => item.numeric + acc, 0); + const total = displayValues + .filter((item) => { + return !item.field.custom.hideFrom.viz; + }) + .reduce((acc, item) => item.display.numeric + acc, 0); - const legendItems = values.map((value, idx) => { + const legendItems = displayValues.map((value, idx) => { + const hidden = value.field.custom.hideFrom.viz; + const display = value.display; return { - label: value.title ?? '', - color: value.color ?? FALLBACK_COLOR, + label: display.title ?? '', + color: display.color ?? FALLBACK_COLOR, yAxis: 1, - getItemKey: () => (value.title ?? '') + idx, + disabled: hidden, + getItemKey: () => (display.title ?? '') + idx, getDisplayValues: () => { const valuesToShow = legendOptions.values ?? []; let displayValues = []; if (valuesToShow.includes(PieChartLegendValues.Value)) { - displayValues.push({ numeric: value.numeric, text: formattedValueToString(value), title: 'Value' }); + displayValues.push({ numeric: display.numeric, text: formattedValueToString(display), title: 'Value' }); } if (valuesToShow.includes(PieChartLegendValues.Percent)) { - const fractionOfTotal = value.numeric / total; + const fractionOfTotal = hidden ? 0 : display.numeric / total; const percentOfTotal = fractionOfTotal * 100; displayValues.push({ numeric: fractionOfTotal, percent: percentOfTotal, - text: percentOfTotal.toFixed(0) + '%', + text: hidden ? '-' : percentOfTotal.toFixed(0) + '%', title: valuesToShow.length > 1 ? 'Percent' : undefined, }); } @@ -128,7 +135,14 @@ function getLegend(props: PieChartProps, displayValues: FieldDisplay[]) { }; }); - return ; + return ( + + ); } function useSliceHighlightState() { @@ -174,7 +188,11 @@ export const PieChartSvg: FC = ({ scroll: true, }); - if (fieldDisplayValues.length < 0) { + const filteredFieldDisplayValues = fieldDisplayValues.filter((dv) => { + return !dv.field.custom.hideFrom.viz; + }); + + if (filteredFieldDisplayValues.length < 0) { return
No data
; } @@ -186,10 +204,12 @@ export const PieChartSvg: FC = ({ const showLabel = displayLabels.length > 0; const showTooltip = tooltipOptions.mode !== 'none' && tooltip.tooltipOpen; - const total = fieldDisplayValues.reduce((acc, item) => item.display.numeric + acc, 0); + const total = filteredFieldDisplayValues.reduce((acc, item) => item.display.numeric + acc, 0); const layout = getPieLayout(width, height, pieType); const colors = [ - ...new Set(fieldDisplayValues.map((fieldDisplayValue) => fieldDisplayValue.display.color ?? FALLBACK_COLOR)), + ...new Set( + filteredFieldDisplayValues.map((fieldDisplayValue) => fieldDisplayValue.display.color ?? FALLBACK_COLOR) + ), ]; return ( @@ -213,7 +233,7 @@ export const PieChartSvg: FC = ({ ); })} = ({ items, displayMode, sortBy: sortKey, + seriesVisibilityChangeBehavior = SeriesVisibilityChangeBehavior.Isolate, sortDesc, - onToggleSort, onLabelClick, + onToggleSort, placement, className, }) => { - const { eventBus } = usePanelContext(); + const { eventBus, onToggleSeriesVisibility } = usePanelContext(); const onMouseEnter = useCallback( (item: VizLegendItem, event: React.MouseEvent) => { @@ -51,6 +53,23 @@ export const VizLegend: React.FunctionComponent = ({ [eventBus] ); + const onLegendLabelClick = useCallback( + (item: VizLegendItem, event: React.MouseEvent) => { + if (onLabelClick) { + onLabelClick(item, event); + } + if (onToggleSeriesVisibility) { + onToggleSeriesVisibility( + item.label, + seriesVisibilityChangeBehavior === SeriesVisibilityChangeBehavior.Hide + ? SeriesVisibilityChangeMode.AppendToSelection + : mapMouseEventToMode(event) + ); + } + }, + [onToggleSeriesVisibility, onLabelClick, seriesVisibilityChangeBehavior] + ); + switch (displayMode) { case LegendDisplayMode.Table: return ( @@ -60,7 +79,7 @@ export const VizLegend: React.FunctionComponent = ({ placement={placement} sortBy={sortKey} sortDesc={sortDesc} - onLabelClick={onLabelClick} + onLabelClick={onLegendLabelClick} onToggleSort={onToggleSort} onLabelMouseEnter={onMouseEnter} onLabelMouseOut={onMouseOut} @@ -74,7 +93,7 @@ export const VizLegend: React.FunctionComponent = ({ placement={placement} onLabelMouseEnter={onMouseEnter} onLabelMouseOut={onMouseOut} - onLabelClick={onLabelClick} + onLabelClick={onLegendLabelClick} /> ); default: diff --git a/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx b/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx index f161d3da610..b958ab1e1ee 100644 --- a/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx +++ b/packages/grafana-ui/src/components/VizLegend/VizLegendList.tsx @@ -15,9 +15,9 @@ export interface Props extends VizLegendBaseProps {} export const VizLegendList: React.FunctionComponent = ({ items, itemRenderer, - onLabelClick, onLabelMouseEnter, onLabelMouseOut, + onLabelClick, placement, className, }) => { diff --git a/packages/grafana-ui/src/components/VizLegend/types.ts b/packages/grafana-ui/src/components/VizLegend/types.ts index 2f1132ec46a..366f1973313 100644 --- a/packages/grafana-ui/src/components/VizLegend/types.ts +++ b/packages/grafana-ui/src/components/VizLegend/types.ts @@ -2,12 +2,18 @@ import { DataFrameFieldIndex, DisplayValue } from '@grafana/data'; import React from 'react'; import { LegendDisplayMode, LegendPlacement } from './models.gen'; +export enum SeriesVisibilityChangeBehavior { + Isolate, + Hide, +} + export interface VizLegendBaseProps { placement: LegendPlacement; className?: string; items: VizLegendItem[]; - itemRenderer?: (item: VizLegendItem, index: number) => JSX.Element; + seriesVisibilityChangeBehavior?: SeriesVisibilityChangeBehavior; onLabelClick?: (item: VizLegendItem, event: React.MouseEvent) => void; + itemRenderer?: (item: VizLegendItem, index: number) => JSX.Element; onLabelMouseEnter?: (item: VizLegendItem, event: React.MouseEvent) => void; onLabelMouseOut?: (item: VizLegendItem, event: React.MouseEvent) => void; } diff --git a/packages/grafana-ui/src/components/VizLegend/utils.ts b/packages/grafana-ui/src/components/VizLegend/utils.ts new file mode 100644 index 00000000000..68cfa418fcd --- /dev/null +++ b/packages/grafana-ui/src/components/VizLegend/utils.ts @@ -0,0 +1,8 @@ +import { SeriesVisibilityChangeMode } from '..'; + +export function mapMouseEventToMode(event: React.MouseEvent): SeriesVisibilityChangeMode { + if (event.ctrlKey || event.metaKey || event.shiftKey) { + return SeriesVisibilityChangeMode.AppendToSelection; + } + return SeriesVisibilityChangeMode.ToggleSelection; +} diff --git a/packages/grafana-ui/src/components/index.ts b/packages/grafana-ui/src/components/index.ts index e99f15b1548..e5ae22e130e 100644 --- a/packages/grafana-ui/src/components/index.ts +++ b/packages/grafana-ui/src/components/index.ts @@ -247,6 +247,7 @@ export { BarChart } from './BarChart/BarChart'; export { TimelineChart } from './Timeline/TimelineChart'; export { BarChartOptions, BarValueVisibility, BarChartFieldConfig } from './BarChart/types'; export { TimelineOptions, TimelineFieldConfig } from './Timeline/types'; -export { GraphNGLegendEvent, GraphNGLegendEventMode } from './GraphNG/types'; +export { GraphNGLegendEvent } from './GraphNG/types'; +export * from './PanelChrome/types'; export * from './NodeGraph'; export { EmotionPerfTest } from './ThemeDemos/EmotionPerfTest'; diff --git a/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx b/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx index dcf575bf342..ccb445600a4 100644 --- a/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx +++ b/packages/grafana-ui/src/components/uPlot/PlotLegend.tsx @@ -5,9 +5,9 @@ import { VizLegendItem } from '../VizLegend/types'; import { VizLegendOptions } from '../VizLegend/models.gen'; import { AxisPlacement } from './config'; import { VizLayout, VizLayoutLegendProps } from '../VizLayout/VizLayout'; -import { mapMouseEventToMode } from '../GraphNG/utils'; import { VizLegend } from '../VizLegend/VizLegend'; import { GraphNGLegendEvent } from '..'; +import { mapMouseEventToMode } from '../VizLegend/utils'; const defaultFormatter = (v: any) => (v == null ? '-' : v.toFixed(1)); diff --git a/packages/grafana-ui/src/components/uPlot/config.ts b/packages/grafana-ui/src/components/uPlot/config.ts index 0bd73176a2a..24f61cf5bac 100644 --- a/packages/grafana-ui/src/components/uPlot/config.ts +++ b/packages/grafana-ui/src/components/uPlot/config.ts @@ -165,7 +165,7 @@ export interface AxisConfig { export interface HideSeriesConfig { tooltip: boolean; legend: boolean; - graph: boolean; + viz: boolean; } /** diff --git a/packages/grafana-ui/src/components/uPlot/utils.test.ts b/packages/grafana-ui/src/components/uPlot/utils.test.ts index 97a3fcec9f7..109fc28d176 100644 --- a/packages/grafana-ui/src/components/uPlot/utils.test.ts +++ b/packages/grafana-ui/src/components/uPlot/utils.test.ts @@ -233,10 +233,10 @@ describe('preparePlotData', () => { { name: 'a', values: [-10, 20, 10], - config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' }, hideFrom: { graph: true } } }, + config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' }, hideFrom: { viz: true } } }, }, { - // Will ignore a series as stacking base as it's hidden from graph + // Will ignore a series as stacking base as it's hidden from viz name: 'b', values: [10, 10, 10], config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackA' } } }, @@ -249,10 +249,10 @@ describe('preparePlotData', () => { { name: 'e', values: [1, 2, 3], - config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackB' }, hideFrom: { graph: true } } }, + config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackB' }, hideFrom: { viz: true } } }, }, { - // Will ignore e series as stacking base as it's hidden from graph + // Will ignore e series as stacking base as it's hidden from viz name: 'f', values: [1, 2, 3], config: { custom: { stacking: { mode: StackingMode.Normal, group: 'stackB' } } }, diff --git a/packages/grafana-ui/src/components/uPlot/utils.ts b/packages/grafana-ui/src/components/uPlot/utils.ts index 703bc709c7c..62f1c1e0962 100755 --- a/packages/grafana-ui/src/components/uPlot/utils.ts +++ b/packages/grafana-ui/src/components/uPlot/utils.ts @@ -96,7 +96,7 @@ export function collectStackingGroups(f: Field, groups: Map, s if ( customConfig.stacking?.mode !== StackingMode.None && customConfig.stacking?.group && - !customConfig.hideFrom?.graph + !customConfig.hideFrom?.viz ) { if (!groups.has(customConfig.stacking.group)) { groups.set(customConfig.stacking.group, [seriesIdx]); diff --git a/public/app/features/dashboard/dashgrid/PanelChrome.tsx b/public/app/features/dashboard/dashgrid/PanelChrome.tsx index 881c3e76d14..b3da31b0ed0 100644 --- a/public/app/features/dashboard/dashgrid/PanelChrome.tsx +++ b/public/app/features/dashboard/dashgrid/PanelChrome.tsx @@ -4,7 +4,7 @@ import classNames from 'classnames'; import { Subscription } from 'rxjs'; // Components import { PanelHeader } from './PanelHeader/PanelHeader'; -import { ErrorBoundary, PanelContextProvider, PanelContext } from '@grafana/ui'; +import { ErrorBoundary, PanelContextProvider, PanelContext, SeriesVisibilityChangeMode } from '@grafana/ui'; // Utils & Services import { getTimeSrv, TimeSrv } from '../services/TimeSrv'; import { applyPanelTimeOverrides } from 'app/features/dashboard/utils/panel'; @@ -30,6 +30,7 @@ import { selectors } from '@grafana/e2e-selectors'; import { loadSnapshotData } from '../utils/loadSnapshotData'; import { RefreshEvent, RenderEvent } from 'app/types/events'; import { changeSeriesColorConfigFactory } from 'app/plugins/panel/timeseries/overrides/colorSeriesConfigFactory'; +import { seriesVisibilityConfigFactory } from './SeriesVisibilityConfigFactory'; const DEFAULT_PLUGIN_ERROR = 'Error in plugin'; @@ -77,6 +78,7 @@ export class PanelChrome extends Component { context: { eventBus, onSeriesColorChange: this.onSeriesColorChange, + onToggleSeriesVisibility: this.onSeriesVisibilityChange, }, data: this.getInitialPanelDataState(), }; @@ -86,6 +88,12 @@ export class PanelChrome extends Component { this.onFieldConfigChange(changeSeriesColorConfigFactory(label, color, this.props.panel.fieldConfig)); }; + onSeriesVisibilityChange = (label: string, mode: SeriesVisibilityChangeMode) => { + this.onFieldConfigChange( + seriesVisibilityConfigFactory(label, mode, this.props.panel.fieldConfig, this.state.data.series) + ); + }; + getInitialPanelDataState(): PanelData { return { state: LoadingState.NotStarted, diff --git a/public/app/features/dashboard/dashgrid/SeriesVisibilityConfigFactory.ts b/public/app/features/dashboard/dashgrid/SeriesVisibilityConfigFactory.ts new file mode 100644 index 00000000000..12c8ad2ce9d --- /dev/null +++ b/public/app/features/dashboard/dashgrid/SeriesVisibilityConfigFactory.ts @@ -0,0 +1,171 @@ +import { + ByNamesMatcherMode, + DataFrame, + DynamicConfigValue, + FieldConfigSource, + FieldMatcherID, + FieldType, + getFieldDisplayName, + isSystemOverrideWithRef, + SystemConfigOverrideRule, +} from '@grafana/data'; +import { SeriesVisibilityChangeMode } from '@grafana/ui'; + +const displayOverrideRef = 'hideSeriesFrom'; +const isHideSeriesOverride = isSystemOverrideWithRef(displayOverrideRef); + +export function seriesVisibilityConfigFactory( + label: string, + mode: SeriesVisibilityChangeMode, + fieldConfig: FieldConfigSource, + data: DataFrame[] +) { + const { overrides } = fieldConfig; + + const displayName = label; + const currentIndex = overrides.findIndex(isHideSeriesOverride); + + if (currentIndex < 0) { + if (mode === SeriesVisibilityChangeMode.ToggleSelection) { + const override = createOverride([displayName]); + + return { + ...fieldConfig, + overrides: [override, ...fieldConfig.overrides], + }; + } + + const displayNames = getDisplayNames(data, displayName); + const override = createOverride(displayNames); + + return { + ...fieldConfig, + overrides: [override, ...fieldConfig.overrides], + }; + } + + const overridesCopy = Array.from(overrides); + const [current] = overridesCopy.splice(currentIndex, 1) as SystemConfigOverrideRule[]; + + if (mode === SeriesVisibilityChangeMode.ToggleSelection) { + const existing = getExistingDisplayNames(current); + + if (existing[0] === displayName && existing.length === 1) { + return { + ...fieldConfig, + overrides: overridesCopy, + }; + } + + const override = createOverride([displayName]); + + return { + ...fieldConfig, + overrides: [override, ...overridesCopy], + }; + } + + const override = createExtendedOverride(current, displayName); + + if (allFieldsAreExcluded(override, data)) { + return { + ...fieldConfig, + overrides: overridesCopy, + }; + } + + return { + ...fieldConfig, + overrides: [override, ...overridesCopy], + }; +} + +function createOverride( + names: string[], + mode = ByNamesMatcherMode.exclude, + property?: DynamicConfigValue +): SystemConfigOverrideRule { + property = property ?? { + id: 'custom.hideFrom', + value: { + viz: true, + legend: false, + tooltip: false, + }, + }; + + return { + __systemRef: displayOverrideRef, + matcher: { + id: FieldMatcherID.byNames, + options: { + mode: mode, + names: names, + prefix: mode === ByNamesMatcherMode.exclude ? 'All except:' : undefined, + readOnly: true, + }, + }, + properties: [ + { + ...property, + value: { + viz: true, + legend: false, + tooltip: false, + }, + }, + ], + }; +} + +const createExtendedOverride = ( + current: SystemConfigOverrideRule, + displayName: string, + mode = ByNamesMatcherMode.exclude +): SystemConfigOverrideRule => { + const property = current.properties.find((p) => p.id === 'custom.hideFrom'); + const existing = getExistingDisplayNames(current); + const index = existing.findIndex((name) => name === displayName); + + if (index < 0) { + existing.push(displayName); + } else { + existing.splice(index, 1); + } + + return createOverride(existing, mode, property); +}; + +const getExistingDisplayNames = (rule: SystemConfigOverrideRule): string[] => { + const names = rule.matcher.options?.names; + if (!Array.isArray(names)) { + return []; + } + return names; +}; + +const allFieldsAreExcluded = (override: SystemConfigOverrideRule, data: DataFrame[]): boolean => { + return getExistingDisplayNames(override).length === getDisplayNames(data).length; +}; + +const getDisplayNames = (data: DataFrame[], excludeName?: string): string[] => { + const unique = new Set(); + + for (const frame of data) { + for (const field of frame.fields) { + if (field.type !== FieldType.number) { + continue; + } + + const name = getFieldDisplayName(field, frame, data); + + if (name === excludeName) { + continue; + } + + unique.add(name); + } + } + + return Array.from(unique); +}; diff --git a/public/app/plugins/panel/piechart/PieChartPanel.tsx b/public/app/plugins/panel/piechart/PieChartPanel.tsx index 167182b332c..bd144afa956 100644 --- a/public/app/plugins/panel/piechart/PieChartPanel.tsx +++ b/public/app/plugins/panel/piechart/PieChartPanel.tsx @@ -10,7 +10,6 @@ export const PieChartPanel: React.FC = ({ height, options, data, - onFieldConfigChange, replaceVariables, fieldConfig, timeZone, diff --git a/public/app/plugins/panel/piechart/module.tsx b/public/app/plugins/panel/piechart/module.tsx index 3408e524b0c..00c69e06216 100644 --- a/public/app/plugins/panel/piechart/module.tsx +++ b/public/app/plugins/panel/piechart/module.tsx @@ -3,6 +3,7 @@ import { PieChartPanel } from './PieChartPanel'; import { PieChartOptions } from './types'; import { LegendDisplayMode, PieChartType, PieChartLabels, PieChartLegendValues } from '@grafana/ui'; import { PieChartPanelChangedHandler } from './migrations'; +import { addHideFrom } from '../timeseries/config'; export const plugin = new PanelPlugin(PieChartPanel) .setPanelChangeHandler(PieChartPanelChangedHandler) @@ -20,6 +21,9 @@ export const plugin = new PanelPlugin(PieChartPanel) }, }, }, + useCustomConfig: (builder) => { + addHideFrom(builder); + }, }) .setPanelOptions((builder) => { builder diff --git a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx index 63fd32f85dc..38fbdd7e63d 100644 --- a/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx +++ b/public/app/plugins/panel/timeseries/TimeSeriesPanel.tsx @@ -1,8 +1,7 @@ import { Field, PanelProps } from '@grafana/data'; -import { TimeSeries, GraphNGLegendEvent, TooltipPlugin, ZoomPlugin } from '@grafana/ui'; +import { TimeSeries, TooltipPlugin, ZoomPlugin } from '@grafana/ui'; import { getFieldLinksForExplore } from 'app/features/explore/utils/links'; -import React, { useCallback } from 'react'; -import { hideSeriesConfigFactory } from './overrides/hideSeriesConfigFactory'; +import React from 'react'; import { AnnotationsPlugin } from './plugins/AnnotationsPlugin'; import { ContextMenuPlugin } from './plugins/ContextMenuPlugin'; import { ExemplarsPlugin } from './plugins/ExemplarsPlugin'; @@ -17,18 +16,9 @@ export const TimeSeriesPanel: React.FC = ({ width, height, options, - fieldConfig, onChangeTimeRange, - onFieldConfigChange, replaceVariables, }) => { - const onLegendClick = useCallback( - (event: GraphNGLegendEvent) => { - onFieldConfigChange(hideSeriesConfigFactory(event, fieldConfig, data.series)); - }, - [fieldConfig, onFieldConfigChange, data.series] - ); - const getFieldLinks = (field: Field, rowIndex: number) => { return getFieldLinksForExplore({ field, rowIndex, range: timeRange }); }; @@ -50,7 +40,6 @@ export const TimeSeriesPanel: React.FC = ({ width={width} height={height} legend={options.legend} - onLegendClick={onLegendClick} > {(config, alignedDataFrame) => { return ( diff --git a/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.test.ts b/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.test.ts index 97fc8e02ce0..369dc0f4a18 100644 --- a/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.test.ts +++ b/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.test.ts @@ -6,13 +6,13 @@ import { FieldType, toDataFrame, } from '@grafana/data'; -import { GraphNGLegendEvent, GraphNGLegendEventMode } from '@grafana/ui'; +import { GraphNGLegendEvent, SeriesVisibilityChangeMode } from '@grafana/ui'; import { hideSeriesConfigFactory } from './hideSeriesConfigFactory'; describe('hideSeriesConfigFactory', () => { it('should create config override matching one series', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.ToggleSelection, + mode: SeriesVisibilityChangeMode.ToggleSelection, fieldIndex: { frameIndex: 0, fieldIndex: 1, @@ -43,7 +43,7 @@ describe('hideSeriesConfigFactory', () => { it('should create config override matching one series if selected with others', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.ToggleSelection, + mode: SeriesVisibilityChangeMode.ToggleSelection, fieldIndex: { frameIndex: 0, fieldIndex: 1, @@ -86,7 +86,7 @@ describe('hideSeriesConfigFactory', () => { it('should create config override that append series to existing override', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.AppendToSelection, + mode: SeriesVisibilityChangeMode.AppendToSelection, fieldIndex: { frameIndex: 1, fieldIndex: 1, @@ -129,7 +129,7 @@ describe('hideSeriesConfigFactory', () => { it('should create config override that hides all series if appending only existing series', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.AppendToSelection, + mode: SeriesVisibilityChangeMode.AppendToSelection, fieldIndex: { frameIndex: 0, fieldIndex: 1, @@ -166,7 +166,7 @@ describe('hideSeriesConfigFactory', () => { it('should create config override that removes series if appending existing field', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.AppendToSelection, + mode: SeriesVisibilityChangeMode.AppendToSelection, fieldIndex: { frameIndex: 0, fieldIndex: 1, @@ -203,7 +203,7 @@ describe('hideSeriesConfigFactory', () => { it('should create config override replacing existing series', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.ToggleSelection, + mode: SeriesVisibilityChangeMode.ToggleSelection, fieldIndex: { frameIndex: 1, fieldIndex: 1, @@ -240,7 +240,7 @@ describe('hideSeriesConfigFactory', () => { it('should create config override removing existing series', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.ToggleSelection, + mode: SeriesVisibilityChangeMode.ToggleSelection, fieldIndex: { frameIndex: 0, fieldIndex: 1, @@ -277,7 +277,7 @@ describe('hideSeriesConfigFactory', () => { it('should remove override if all fields are appended', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.AppendToSelection, + mode: SeriesVisibilityChangeMode.AppendToSelection, fieldIndex: { frameIndex: 1, fieldIndex: 1, @@ -314,7 +314,7 @@ describe('hideSeriesConfigFactory', () => { it('should create config override hiding appended series if no previous override exists', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.AppendToSelection, + mode: SeriesVisibilityChangeMode.AppendToSelection, fieldIndex: { frameIndex: 0, fieldIndex: 1, @@ -357,7 +357,7 @@ describe('hideSeriesConfigFactory', () => { it('should return existing override if invalid index is passed', () => { const event: GraphNGLegendEvent = { - mode: GraphNGLegendEventMode.ToggleSelection, + mode: SeriesVisibilityChangeMode.ToggleSelection, fieldIndex: { frameIndex: 4, fieldIndex: 1, diff --git a/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.ts b/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.ts index 88b6ca1a8e8..c4e546c1b1b 100644 --- a/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.ts +++ b/public/app/plugins/panel/timeseries/overrides/hideSeriesConfigFactory.ts @@ -9,7 +9,7 @@ import { isSystemOverrideWithRef, SystemConfigOverrideRule, } from '@grafana/data'; -import { GraphNGLegendEvent, GraphNGLegendEventMode } from '@grafana/ui'; +import { GraphNGLegendEvent, SeriesVisibilityChangeMode } from '@grafana/ui'; const displayOverrideRef = 'hideSeriesFrom'; const isHideSeriesOverride = isSystemOverrideWithRef(displayOverrideRef); @@ -38,7 +38,7 @@ export const hideSeriesConfigFactory = ( const currentIndex = overrides.findIndex(isHideSeriesOverride); if (currentIndex < 0) { - if (mode === GraphNGLegendEventMode.ToggleSelection) { + if (mode === SeriesVisibilityChangeMode.ToggleSelection) { const override = createOverride([displayName]); return { @@ -59,7 +59,7 @@ export const hideSeriesConfigFactory = ( const overridesCopy = Array.from(overrides); const [current] = overridesCopy.splice(currentIndex, 1) as SystemConfigOverrideRule[]; - if (mode === GraphNGLegendEventMode.ToggleSelection) { + if (mode === SeriesVisibilityChangeMode.ToggleSelection) { const existing = getExistingDisplayNames(current); if (existing[0] === displayName && existing.length === 1) {