mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
BarChart: Highlight bars option for easier interaction (#60530)
* Add bar highlighter plugin to BarChart * refactor feature * Horizontal bars can also be hovered * Tooltip mode UI reflects settings when stacked and bar highlight toggled * rename variables * override tooltip option + zIndex on hover box * change option desc and simplify condition
This commit is contained in:
parent
f9daf61e96
commit
2cf628e37a
@ -20,6 +20,7 @@ import {
|
|||||||
measureText,
|
measureText,
|
||||||
PlotLegend,
|
PlotLegend,
|
||||||
Portal,
|
Portal,
|
||||||
|
StackingMode,
|
||||||
TooltipDisplayMode,
|
TooltipDisplayMode,
|
||||||
UPlotConfigBuilder,
|
UPlotConfigBuilder,
|
||||||
UPLOT_AXIS_FONT_SIZE,
|
UPLOT_AXIS_FONT_SIZE,
|
||||||
@ -168,6 +169,8 @@ export const BarChartPanel: React.FunctionComponent<Props> = ({
|
|||||||
const disp = getFieldDisplayName(field, alignedFrame);
|
const disp = getFieldDisplayName(field, alignedFrame);
|
||||||
seriesIdx = info.aligned.fields.findIndex((f) => disp === getFieldDisplayName(f, info.aligned));
|
seriesIdx = info.aligned.fields.findIndex((f) => disp === getFieldDisplayName(f, info.aligned));
|
||||||
}
|
}
|
||||||
|
const tooltipMode =
|
||||||
|
options.fullHighlight && options.stacking !== StackingMode.None ? TooltipDisplayMode.Multi : options.tooltip.mode;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -195,7 +198,7 @@ export const BarChartPanel: React.FunctionComponent<Props> = ({
|
|||||||
rowIndex={datapointIdx}
|
rowIndex={datapointIdx}
|
||||||
columnIndex={seriesIdx}
|
columnIndex={seriesIdx}
|
||||||
sortOrder={options.tooltip.sort}
|
sortOrder={options.tooltip.sort}
|
||||||
mode={options.tooltip.mode}
|
mode={tooltipMode}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
@ -280,6 +283,7 @@ export const BarChartPanel: React.FunctionComponent<Props> = ({
|
|||||||
text,
|
text,
|
||||||
xTickLabelRotation,
|
xTickLabelRotation,
|
||||||
xTickLabelSpacing,
|
xTickLabelSpacing,
|
||||||
|
fullHighlight,
|
||||||
} = options;
|
} = options;
|
||||||
|
|
||||||
return preparePlotConfigBuilder({
|
return preparePlotConfigBuilder({
|
||||||
@ -305,6 +309,7 @@ export const BarChartPanel: React.FunctionComponent<Props> = ({
|
|||||||
getColor,
|
getColor,
|
||||||
fillOpacity,
|
fillOpacity,
|
||||||
allFrames: info.viz,
|
allFrames: info.viz,
|
||||||
|
fullHighlight,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ export interface BarsOptions {
|
|||||||
xSpacing?: number;
|
xSpacing?: number;
|
||||||
xTimeAuto?: boolean;
|
xTimeAuto?: boolean;
|
||||||
negY?: boolean[];
|
negY?: boolean[];
|
||||||
|
fullHighlight?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -315,6 +316,17 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let barRect = { x: lft, y: top, w: wid, h: hgt, sidx: seriesIdx, didx: dataIdx };
|
let barRect = { x: lft, y: top, w: wid, h: hgt, sidx: seriesIdx, didx: dataIdx };
|
||||||
|
|
||||||
|
if (opts.fullHighlight) {
|
||||||
|
if (opts.xOri === ScaleOrientation.Horizontal) {
|
||||||
|
barRect.y = 0;
|
||||||
|
barRect.h = u.bbox.height;
|
||||||
|
} else {
|
||||||
|
barRect.x = 0;
|
||||||
|
barRect.w = u.bbox.width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
qt.add(barRect);
|
qt.add(barRect);
|
||||||
|
|
||||||
if (showValue !== VisibilityMode.Never) {
|
if (showValue !== VisibilityMode.Never) {
|
||||||
@ -429,6 +441,9 @@ export function getConfig(opts: BarsOptions, theme: GrafanaTheme2) {
|
|||||||
u.root.querySelectorAll('.u-cursor-pt').forEach((el) => {
|
u.root.querySelectorAll('.u-cursor-pt').forEach((el) => {
|
||||||
if (el instanceof HTMLElement) {
|
if (el instanceof HTMLElement) {
|
||||||
el.style.borderRadius = '0';
|
el.style.borderRadius = '0';
|
||||||
|
if (opts.fullHighlight) {
|
||||||
|
el.style.zIndex = '-1';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
@ -53,6 +53,8 @@ Panel: thema.#Lineage & {
|
|||||||
barWidth: float64 & >= 0 & <= 1 | *0.97
|
barWidth: float64 & >= 0 & <= 1 | *0.97
|
||||||
// Controls the width of groups. 1 = max with, 0 = min width.
|
// Controls the width of groups. 1 = max with, 0 = min width.
|
||||||
groupWidth: float64 & >= 0 & <= 1 | *0.7
|
groupWidth: float64 & >= 0 & <= 1 | *0.7
|
||||||
|
// Enables mode which highlights the entire bar area and shows tooltip when cursor hovers over highlighted area
|
||||||
|
fullHighlight: bool | *false
|
||||||
} @cuetsy(kind="interface")
|
} @cuetsy(kind="interface")
|
||||||
PanelFieldConfig: {
|
PanelFieldConfig: {
|
||||||
ui.AxisConfig
|
ui.AxisConfig
|
||||||
|
@ -25,6 +25,10 @@ export interface PanelOptions extends ui.OptionsWithLegend, ui.OptionsWithToolti
|
|||||||
* TODO docs
|
* TODO docs
|
||||||
*/
|
*/
|
||||||
colorByField?: string;
|
colorByField?: string;
|
||||||
|
/**
|
||||||
|
* Enables mode which highlights the entire bar area and shows tooltip when cursor hovers over highlighted area
|
||||||
|
*/
|
||||||
|
fullHighlight: boolean;
|
||||||
/**
|
/**
|
||||||
* Controls the width of groups. 1 = max with, 0 = min width.
|
* Controls the width of groups. 1 = max with, 0 = min width.
|
||||||
*/
|
*/
|
||||||
@ -63,6 +67,7 @@ export interface PanelOptions extends ui.OptionsWithLegend, ui.OptionsWithToolti
|
|||||||
export const defaultPanelOptions: Partial<PanelOptions> = {
|
export const defaultPanelOptions: Partial<PanelOptions> = {
|
||||||
barRadius: 0,
|
barRadius: 0,
|
||||||
barWidth: 0.97,
|
barWidth: 0.97,
|
||||||
|
fullHighlight: false,
|
||||||
groupWidth: 0.7,
|
groupWidth: 0.7,
|
||||||
orientation: ui.VizOrientation.Auto,
|
orientation: ui.VizOrientation.Auto,
|
||||||
showValue: ui.VisibilityMode.Auto,
|
showValue: ui.VisibilityMode.Auto,
|
||||||
|
@ -220,6 +220,11 @@ export const plugin = new PanelPlugin<PanelOptions, PanelFieldConfig>(BarChartPa
|
|||||||
max: 0.5,
|
max: 0.5,
|
||||||
step: 0.05,
|
step: 0.05,
|
||||||
},
|
},
|
||||||
|
})
|
||||||
|
.addBooleanSwitch({
|
||||||
|
path: 'fullHighlight',
|
||||||
|
name: 'Highlight full area on hover',
|
||||||
|
defaultValue: defaultPanelOptions.fullHighlight,
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addFieldNamePicker({
|
builder.addFieldNamePicker({
|
||||||
@ -228,7 +233,10 @@ export const plugin = new PanelPlugin<PanelOptions, PanelFieldConfig>(BarChartPa
|
|||||||
description: 'Use the color value for a sibling field to color each bar value.',
|
description: 'Use the color value for a sibling field to color each bar value.',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (!context.options?.fullHighlight || context.options?.stacking === StackingMode.None) {
|
||||||
commonOptionsBuilder.addTooltipOptions(builder);
|
commonOptionsBuilder.addTooltipOptions(builder);
|
||||||
|
}
|
||||||
|
|
||||||
commonOptionsBuilder.addLegendOptions(builder);
|
commonOptionsBuilder.addLegendOptions(builder);
|
||||||
commonOptionsBuilder.addTextSizeOptions(builder, false);
|
commonOptionsBuilder.addTextSizeOptions(builder, false);
|
||||||
})
|
})
|
||||||
|
@ -108,6 +108,7 @@ describe('BarChart utils', () => {
|
|||||||
text: {
|
text: {
|
||||||
valueSize: 10,
|
valueSize: 10,
|
||||||
},
|
},
|
||||||
|
fullHighlight: false,
|
||||||
rawValue: (seriesIdx: number, valueIdx: number) => frame.fields[seriesIdx].values.get(valueIdx),
|
rawValue: (seriesIdx: number, valueIdx: number) => frame.fields[seriesIdx].values.get(valueIdx),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -79,6 +79,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<BarChartOptionsEX> = ({
|
|||||||
xTickLabelSpacing = 0,
|
xTickLabelSpacing = 0,
|
||||||
legend,
|
legend,
|
||||||
timeZone,
|
timeZone,
|
||||||
|
fullHighlight,
|
||||||
}) => {
|
}) => {
|
||||||
const builder = new UPlotConfigBuilder();
|
const builder = new UPlotConfigBuilder();
|
||||||
|
|
||||||
@ -118,6 +119,7 @@ export const preparePlotConfigBuilder: UPlotConfigPrepFn<BarChartOptionsEX> = ({
|
|||||||
xSpacing: xTickLabelSpacing,
|
xSpacing: xTickLabelSpacing,
|
||||||
xTimeAuto: frame.fields[0]?.type === FieldType.time && !frame.fields[0].config.unit?.startsWith('time:'),
|
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),
|
negY: frame.fields.map((f) => f.config.custom?.transform === GraphTransform.NegativeY),
|
||||||
|
fullHighlight,
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = getConfig(opts, theme);
|
const config = getConfig(opts, theme);
|
||||||
|
Loading…
Reference in New Issue
Block a user