diff --git a/public/app/plugins/panel/barchart/bars.ts b/public/app/plugins/panel/barchart/bars.ts index 7416ba55b7b..1e2282ef82b 100644 --- a/public/app/plugins/panel/barchart/bars.ts +++ b/public/app/plugins/panel/barchart/bars.ts @@ -57,6 +57,7 @@ export interface BarsOptions { legend?: VizLegendOptions; xSpacing?: number; xTimeAuto?: boolean; + negY?: boolean[]; } /** @@ -352,6 +353,10 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) { let middleShift = isXHorizontal ? 0 : -Math.round(MIDDLE_BASELINE_SHIFT * fontSize); let value = rawValue(seriesIdx, dataIdx); + if (opts.negY?.[seriesIdx] && value != null) { + value *= -1; + } + if (value != null) { // Calculate final co-ordinates for text position const x = @@ -380,7 +385,7 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) { // Adjust for baseline which is "top" in this case xAdjust = (textMetrics.width * scaleFactor) / 2; - // yAdjust only matters when when the value isn't negative + // yAdjust only matters when the value isn't negative yAdjust = value > 0 ? (textMetrics.actualBoundingBoxAscent + textMetrics.actualBoundingBoxDescent) * scaleFactor @@ -516,7 +521,12 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) { for (const sidx in labels[didx]) { const label = labels[didx][sidx]; - const { text, value, x = 0, y = 0 } = label; + const { text, x = 0, y = 0 } = label; + let { value } = label; + + if (opts.negY?.[sidx] && value != null) { + value *= -1; + } let align: CanvasTextAlign = isXHorizontal ? 'center' : value !== null && value < 0 ? 'right' : 'left'; let baseline: CanvasTextBaseline = isXHorizontal diff --git a/public/app/plugins/panel/barchart/module.tsx b/public/app/plugins/panel/barchart/module.tsx index a371f146499..9a199b7e328 100644 --- a/public/app/plugins/panel/barchart/module.tsx +++ b/public/app/plugins/panel/barchart/module.tsx @@ -8,7 +8,7 @@ import { VizOrientation, } from '@grafana/data'; import { config } from '@grafana/runtime'; -import { StackingMode, VisibilityMode } from '@grafana/schema'; +import { GraphTransform, StackingMode, VisibilityMode } from '@grafana/schema'; import { graphFieldOptions, commonOptionsBuilder } from '@grafana/ui'; import { BarChartPanel } from './BarChartPanel'; @@ -63,6 +63,28 @@ export const plugin = new PanelPlugin(BarChartPa }, }); + builder.addSelect({ + category: ['Graph styles'], + name: 'Transform', + path: 'transform', + settings: { + options: [ + { + label: 'Constant', + value: GraphTransform.Constant, + description: 'The first value will be shown as a constant line', + }, + { + label: 'Negative Y', + value: GraphTransform.NegativeY, + description: 'Flip the results to negative values on the y axis', + }, + ], + isClearable: true, + }, + hideFromDefaults: true, + }); + commonOptionsBuilder.addAxisConfig(builder, cfg, false); commonOptionsBuilder.addHideFrom(builder); }, diff --git a/public/app/plugins/panel/barchart/utils.ts b/public/app/plugins/panel/barchart/utils.ts index 56c07cb04b3..dfe5222d2dc 100644 --- a/public/app/plugins/panel/barchart/utils.ts +++ b/public/app/plugins/panel/barchart/utils.ts @@ -18,6 +18,7 @@ import { import { maybeSortFrame } from '@grafana/data/src/transformations/transformers/joinDataFrames'; import { AxisPlacement, + GraphTransform, ScaleDirection, ScaleDistribution, ScaleOrientation, @@ -107,6 +108,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn = ({ legend, xSpacing: xTickLabelSpacing, xTimeAuto: frame.fields[0]?.type === FieldType.time && !frame.fields[0].config.unit?.startsWith('time:'), + negY: frame.fields.map((f) => f.config.custom?.transform === GraphTransform.NegativeY), }; const config = getConfig(opts, theme);