mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
GraphNG: support auto and explicit axis width (#29553)
* auto axis * auto axis * expand everyhting with the same scale/unit * expand everyhting with the same scale/unit
This commit is contained in:
parent
60502463e2
commit
3c9310e93c
@ -114,14 +114,15 @@ export const GraphNG: React.FC<GraphNGProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
const fmt = field.display ?? defaultFormatter;
|
const fmt = field.display ?? defaultFormatter;
|
||||||
const scale = config.unit || '__fixed';
|
const scaleKey = config.unit || '__fixed';
|
||||||
const isNewScale = !builder.hasScale(scale);
|
|
||||||
|
|
||||||
if (isNewScale && customConfig.axisPlacement !== AxisPlacement.Hidden) {
|
if (customConfig.axisPlacement !== AxisPlacement.Hidden) {
|
||||||
builder.addScale({ scaleKey: scale, min: field.config.min, max: field.config.max });
|
// The builder will manage unique scaleKeys and combine where appropriate
|
||||||
|
builder.addScale({ scaleKey, min: field.config.min, max: field.config.max });
|
||||||
builder.addAxis({
|
builder.addAxis({
|
||||||
scaleKey: scale,
|
scaleKey,
|
||||||
label: customConfig.axisLabel,
|
label: customConfig.axisLabel,
|
||||||
|
size: customConfig.axisWidth,
|
||||||
placement: customConfig.axisPlacement ?? AxisPlacement.Auto,
|
placement: customConfig.axisPlacement ?? AxisPlacement.Auto,
|
||||||
formatValue: v => formattedValueToString(fmt(v)),
|
formatValue: v => formattedValueToString(fmt(v)),
|
||||||
theme,
|
theme,
|
||||||
@ -136,7 +137,7 @@ export const GraphNG: React.FC<GraphNGProps> = ({
|
|||||||
const pointsMode = customConfig.mode === GraphMode.Points ? PointMode.Always : customConfig.points;
|
const pointsMode = customConfig.mode === GraphMode.Points ? PointMode.Always : customConfig.points;
|
||||||
|
|
||||||
builder.addSeries({
|
builder.addSeries({
|
||||||
scaleKey: scale,
|
scaleKey,
|
||||||
mode: customConfig.mode!,
|
mode: customConfig.mode!,
|
||||||
lineColor: seriesColor,
|
lineColor: seriesColor,
|
||||||
lineWidth: customConfig.lineWidth,
|
lineWidth: customConfig.lineWidth,
|
||||||
@ -149,7 +150,7 @@ export const GraphNG: React.FC<GraphNGProps> = ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (hasLegend.current) {
|
if (hasLegend.current) {
|
||||||
const axisPlacement = builder.getAxisPlacement(scale);
|
const axisPlacement = builder.getAxisPlacement(scaleKey);
|
||||||
|
|
||||||
legendItems.push({
|
legendItems.push({
|
||||||
color: seriesColor,
|
color: seriesColor,
|
||||||
|
@ -3,13 +3,14 @@ import uPlot, { Axis } from 'uplot';
|
|||||||
import { PlotConfigBuilder } from '../types';
|
import { PlotConfigBuilder } from '../types';
|
||||||
import { measureText } from '../../../utils/measureText';
|
import { measureText } from '../../../utils/measureText';
|
||||||
import { AxisPlacement } from '../config';
|
import { AxisPlacement } from '../config';
|
||||||
|
import { optMinMax } from './UPlotScaleBuilder';
|
||||||
|
|
||||||
export interface AxisProps {
|
export interface AxisProps {
|
||||||
scaleKey: string;
|
scaleKey: string;
|
||||||
theme: GrafanaTheme;
|
theme: GrafanaTheme;
|
||||||
label?: string;
|
label?: string;
|
||||||
show?: boolean;
|
show?: boolean;
|
||||||
size?: number;
|
size?: number | null;
|
||||||
placement?: AxisPlacement;
|
placement?: AxisPlacement;
|
||||||
grid?: boolean;
|
grid?: boolean;
|
||||||
formatValue?: (v: any) => string;
|
formatValue?: (v: any) => string;
|
||||||
@ -19,6 +20,16 @@ export interface AxisProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UPlotAxisBuilder extends PlotConfigBuilder<AxisProps, Axis> {
|
export class UPlotAxisBuilder extends PlotConfigBuilder<AxisProps, Axis> {
|
||||||
|
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 {
|
getConfig(): Axis {
|
||||||
const {
|
const {
|
||||||
scaleKey,
|
scaleKey,
|
||||||
@ -42,7 +53,7 @@ export class UPlotAxisBuilder extends PlotConfigBuilder<AxisProps, Axis> {
|
|||||||
side: getUPlotSideFromAxis(placement),
|
side: getUPlotSideFromAxis(placement),
|
||||||
font: `12px 'Roboto'`,
|
font: `12px 'Roboto'`,
|
||||||
labelFont: `12px 'Roboto'`,
|
labelFont: `12px 'Roboto'`,
|
||||||
size: calculateAxisSize,
|
size: this.props.size ?? calculateAxisSize,
|
||||||
grid: {
|
grid: {
|
||||||
show: grid,
|
show: grid,
|
||||||
stroke: gridColor,
|
stroke: gridColor,
|
||||||
@ -107,8 +118,7 @@ function calculateAxisSize(self: uPlot, values: string[], axisIdx: number) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let axisWidth = measureText(maxLength, 12).width + 18;
|
return measureText(maxLength, 12).width + 18;
|
||||||
return axisWidth;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Format time axis ticks */
|
/** Format time axis ticks */
|
||||||
|
@ -61,7 +61,6 @@ describe('UPlotConfigBuilder', () => {
|
|||||||
formatValue: () => 'test value',
|
formatValue: () => 'test value',
|
||||||
grid: false,
|
grid: false,
|
||||||
show: true,
|
show: true,
|
||||||
size: 1,
|
|
||||||
theme: { isDark: true, palette: { gray25: '#ffffff' }, colors: { text: 'gray' } } as GrafanaTheme,
|
theme: { isDark: true, palette: { gray25: '#ffffff' }, colors: { text: 'gray' } } as GrafanaTheme,
|
||||||
values: [],
|
values: [],
|
||||||
});
|
});
|
||||||
|
@ -8,13 +8,17 @@ export class UPlotConfigBuilder {
|
|||||||
private series: UPlotSeriesBuilder[] = [];
|
private series: UPlotSeriesBuilder[] = [];
|
||||||
private axes: Record<string, UPlotAxisBuilder> = {};
|
private axes: Record<string, UPlotAxisBuilder> = {};
|
||||||
private scales: UPlotScaleBuilder[] = [];
|
private scales: UPlotScaleBuilder[] = [];
|
||||||
private registeredScales: string[] = [];
|
|
||||||
|
|
||||||
hasLeftAxis = false;
|
hasLeftAxis = false;
|
||||||
|
|
||||||
addAxis(props: AxisProps) {
|
addAxis(props: AxisProps) {
|
||||||
props.placement = props.placement ?? AxisPlacement.Auto;
|
props.placement = props.placement ?? AxisPlacement.Auto;
|
||||||
|
|
||||||
|
if (this.axes[props.scaleKey]) {
|
||||||
|
this.axes[props.scaleKey].merge(props);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Handle auto placement logic
|
// Handle auto placement logic
|
||||||
if (props.placement === AxisPlacement.Auto) {
|
if (props.placement === AxisPlacement.Auto) {
|
||||||
props.placement = this.hasLeftAxis ? AxisPlacement.Right : AxisPlacement.Left;
|
props.placement = this.hasLeftAxis ? AxisPlacement.Right : AxisPlacement.Left;
|
||||||
@ -36,15 +40,16 @@ export class UPlotConfigBuilder {
|
|||||||
this.series.push(new UPlotSeriesBuilder(props));
|
this.series.push(new UPlotSeriesBuilder(props));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Add or update the scale with the scale key */
|
||||||
addScale(props: ScaleProps) {
|
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));
|
this.scales.push(new UPlotScaleBuilder(props));
|
||||||
}
|
}
|
||||||
|
|
||||||
hasScale(scaleKey: string) {
|
|
||||||
return this.registeredScales.indexOf(scaleKey) > -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
getConfig() {
|
getConfig() {
|
||||||
const config: PlotSeriesConfig = { series: [{}] };
|
const config: PlotSeriesConfig = { series: [{}] };
|
||||||
config.axes = Object.values(this.axes).map(a => a.getConfig());
|
config.axes = Object.values(this.axes).map(a => a.getConfig());
|
||||||
|
@ -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));
|
||||||
|
});
|
||||||
|
});
|
@ -9,6 +9,11 @@ export interface ScaleProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
|
export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
|
||||||
|
merge(props: ScaleProps) {
|
||||||
|
this.props.min = optMinMax('min', this.props.min, props.min);
|
||||||
|
this.props.max = optMinMax('max', this.props.max, props.max);
|
||||||
|
}
|
||||||
|
|
||||||
getConfig() {
|
getConfig() {
|
||||||
const { isTime, scaleKey } = this.props;
|
const { isTime, scaleKey } = this.props;
|
||||||
if (isTime) {
|
if (isTime) {
|
||||||
@ -29,3 +34,18 @@ export class UPlotScaleBuilder extends PlotConfigBuilder<ScaleProps, Scale> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
@ -107,9 +107,8 @@ export const plugin = new PanelPlugin<Options, GraphFieldConfig>(GraphPanel)
|
|||||||
path: 'axisWidth',
|
path: 'axisWidth',
|
||||||
name: 'Width',
|
name: 'Width',
|
||||||
category: ['Axis'],
|
category: ['Axis'],
|
||||||
defaultValue: 60,
|
|
||||||
settings: {
|
settings: {
|
||||||
placeholder: '60',
|
placeholder: 'Auto',
|
||||||
},
|
},
|
||||||
showIf: c => c.axisPlacement !== AxisPlacement.Hidden,
|
showIf: c => c.axisPlacement !== AxisPlacement.Hidden,
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user