diff --git a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx index 25a9a65b752..fa57563d812 100755 --- a/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx +++ b/packages/grafana-ui/src/components/GraphNG/GraphNG.tsx @@ -114,14 +114,15 @@ export const GraphNG: React.FC = ({ } const fmt = field.display ?? defaultFormatter; - const scale = config.unit || '__fixed'; - const isNewScale = !builder.hasScale(scale); + const scaleKey = config.unit || '__fixed'; - if (isNewScale && customConfig.axisPlacement !== AxisPlacement.Hidden) { - builder.addScale({ scaleKey: scale, min: field.config.min, max: field.config.max }); + if (customConfig.axisPlacement !== AxisPlacement.Hidden) { + // The builder will manage unique scaleKeys and combine where appropriate + builder.addScale({ scaleKey, min: field.config.min, max: field.config.max }); builder.addAxis({ - scaleKey: scale, + scaleKey, label: customConfig.axisLabel, + size: customConfig.axisWidth, placement: customConfig.axisPlacement ?? AxisPlacement.Auto, formatValue: v => formattedValueToString(fmt(v)), theme, @@ -136,7 +137,7 @@ export const GraphNG: React.FC = ({ const pointsMode = customConfig.mode === GraphMode.Points ? PointMode.Always : customConfig.points; builder.addSeries({ - scaleKey: scale, + scaleKey, mode: customConfig.mode!, lineColor: seriesColor, lineWidth: customConfig.lineWidth, @@ -149,7 +150,7 @@ export const GraphNG: React.FC = ({ }); if (hasLegend.current) { - const axisPlacement = builder.getAxisPlacement(scale); + const axisPlacement = builder.getAxisPlacement(scaleKey); legendItems.push({ color: seriesColor, diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts index 8792d474d0d..4c33ae7546c 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotAxisBuilder.ts @@ -3,13 +3,14 @@ import uPlot, { Axis } from 'uplot'; import { PlotConfigBuilder } from '../types'; import { measureText } from '../../../utils/measureText'; import { AxisPlacement } from '../config'; +import { optMinMax } from './UPlotScaleBuilder'; export interface AxisProps { scaleKey: string; theme: GrafanaTheme; label?: string; show?: boolean; - size?: number; + size?: number | null; placement?: AxisPlacement; grid?: boolean; formatValue?: (v: any) => string; @@ -19,6 +20,16 @@ export interface AxisProps { } export class UPlotAxisBuilder extends PlotConfigBuilder { + merge(props: AxisProps) { + this.props.size = optMinMax('max', this.props.size, props.size); + if (!this.props.label) { + this.props.label = props.label; + } + if (this.props.placement === AxisPlacement.Auto) { + this.props.placement = props.placement; + } + } + getConfig(): Axis { const { scaleKey, @@ -42,7 +53,7 @@ export class UPlotAxisBuilder extends PlotConfigBuilder { side: getUPlotSideFromAxis(placement), font: `12px 'Roboto'`, labelFont: `12px 'Roboto'`, - size: calculateAxisSize, + size: this.props.size ?? calculateAxisSize, grid: { show: grid, stroke: gridColor, @@ -107,8 +118,7 @@ function calculateAxisSize(self: uPlot, values: string[], axisIdx: number) { } } - let axisWidth = measureText(maxLength, 12).width + 18; - return axisWidth; + return measureText(maxLength, 12).width + 18; } /** Format time axis ticks */ diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts index 18a3ae243e9..3973719f175 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.test.ts @@ -61,7 +61,6 @@ describe('UPlotConfigBuilder', () => { formatValue: () => 'test value', grid: false, show: true, - size: 1, theme: { isDark: true, palette: { gray25: '#ffffff' }, colors: { text: 'gray' } } as GrafanaTheme, values: [], }); diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts index e12232bfc5f..3896fa97bce 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotConfigBuilder.ts @@ -8,13 +8,17 @@ export class UPlotConfigBuilder { private series: UPlotSeriesBuilder[] = []; private axes: Record = {}; private scales: UPlotScaleBuilder[] = []; - private registeredScales: string[] = []; hasLeftAxis = false; addAxis(props: AxisProps) { props.placement = props.placement ?? AxisPlacement.Auto; + if (this.axes[props.scaleKey]) { + this.axes[props.scaleKey].merge(props); + return; + } + // Handle auto placement logic if (props.placement === AxisPlacement.Auto) { props.placement = this.hasLeftAxis ? AxisPlacement.Right : AxisPlacement.Left; @@ -36,15 +40,16 @@ export class UPlotConfigBuilder { this.series.push(new UPlotSeriesBuilder(props)); } + /** Add or update the scale with the scale key */ addScale(props: ScaleProps) { - this.registeredScales.push(props.scaleKey); + const current = this.scales.find(v => v.props.scaleKey === props.scaleKey); + if (current) { + current.merge(props); + return; + } this.scales.push(new UPlotScaleBuilder(props)); } - hasScale(scaleKey: string) { - return this.registeredScales.indexOf(scaleKey) > -1; - } - getConfig() { const config: PlotSeriesConfig = { series: [{}] }; config.axes = Object.values(this.axes).map(a => a.getConfig()); diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.test.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.test.ts new file mode 100644 index 00000000000..82576389c82 --- /dev/null +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.test.ts @@ -0,0 +1,21 @@ +import { optMinMax } from './UPlotScaleBuilder'; + +describe('UPlotScaleBuilder', () => { + it('opt min max', () => { + expect(7).toEqual(optMinMax('min', null, 7)); + expect(7).toEqual(optMinMax('min', undefined, 7)); + expect(7).toEqual(optMinMax('min', 20, 7)); + + expect(7).toEqual(optMinMax('min', 7, null)); + expect(7).toEqual(optMinMax('min', 7, undefined)); + expect(7).toEqual(optMinMax('min', 7, 20)); + + expect(7).toEqual(optMinMax('max', null, 7)); + expect(7).toEqual(optMinMax('max', undefined, 7)); + expect(7).toEqual(optMinMax('max', 5, 7)); + + expect(7).toEqual(optMinMax('max', 7, null)); + expect(7).toEqual(optMinMax('max', 7, undefined)); + expect(7).toEqual(optMinMax('max', 7, 5)); + }); +}); diff --git a/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts index e3f822b422b..16f2f2caef4 100644 --- a/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts +++ b/packages/grafana-ui/src/components/uPlot/config/UPlotScaleBuilder.ts @@ -9,6 +9,11 @@ export interface ScaleProps { } export class UPlotScaleBuilder extends PlotConfigBuilder { + merge(props: ScaleProps) { + this.props.min = optMinMax('min', this.props.min, props.min); + this.props.max = optMinMax('max', this.props.max, props.max); + } + getConfig() { const { isTime, scaleKey } = this.props; if (isTime) { @@ -29,3 +34,18 @@ export class UPlotScaleBuilder extends PlotConfigBuilder { }; } } + +export function optMinMax(minmax: 'min' | 'max', a?: number | null, b?: number | null): undefined | number | null { + const hasA = !(a === undefined || a === null); + const hasB = !(b === undefined || b === null); + if (hasA) { + if (!hasB) { + return a; + } + if (minmax === 'min') { + return a! < b! ? a : b; + } + return a! > b! ? a : b; + } + return b; +} diff --git a/public/app/plugins/panel/graph3/module.tsx b/public/app/plugins/panel/graph3/module.tsx index c2d0128e916..2ceee4b145a 100644 --- a/public/app/plugins/panel/graph3/module.tsx +++ b/public/app/plugins/panel/graph3/module.tsx @@ -107,9 +107,8 @@ export const plugin = new PanelPlugin(GraphPanel) path: 'axisWidth', name: 'Width', category: ['Axis'], - defaultValue: 60, settings: { - placeholder: '60', + placeholder: 'Auto', }, showIf: c => c.axisPlacement !== AxisPlacement.Hidden, });