mirror of https://github.com/buster-so/buster.git
Merge pull request #1221 from buster-so/big-nate-bus-1985-colorby-tooltip-and-legend-defaults
tooltip adjustments for color by and skip null
This commit is contained in:
commit
e9d86488c6
|
@ -1,4 +1,4 @@
|
|||
import type { ChartConfigProps, ChartEncodes } from '@buster/server-shared/metrics';
|
||||
import type { BarAndLineAxis, ChartConfigProps, ChartEncodes } from '@buster/server-shared/metrics';
|
||||
import type { ChartType as ChartJSChartType, PluginChartOptions } from 'chart.js';
|
||||
import type { AnnotationPluginOptions } from 'chartjs-plugin-annotation';
|
||||
import { useMemo } from 'react';
|
||||
|
@ -203,9 +203,10 @@ export const useOptions = ({
|
|||
const options: ChartProps<ChartJSChartType>['options'] = useMemo(() => {
|
||||
const chartAnnotations = chartPlugins?.annotation?.annotations;
|
||||
const isLargeDataset = numberOfDataPoints > LINE_DECIMATION_THRESHOLD;
|
||||
const hasColorBy = (selectedAxis as BarAndLineAxis).colorBy?.length > 0;
|
||||
|
||||
return {
|
||||
skipNull: true,
|
||||
skipNull: hasColorBy,
|
||||
indexAxis: isHorizontalBar ? 'y' : 'x',
|
||||
backgroundColor: colors,
|
||||
borderColor: colors,
|
||||
|
|
|
@ -48,17 +48,37 @@ export const BusterChartJSTooltip: React.FC<{
|
|||
return undefined;
|
||||
}, [isBar, barGroupType, isLine, lineGroupType]);
|
||||
|
||||
//@ts-expect-error - skipNull is not typed, only for some charts but yolo
|
||||
const skipNull = chart.options.skipNull === true;
|
||||
|
||||
const hasMultipleShownDatasets = useMemo(() => {
|
||||
const nonHiddenDatasets = datasets.filter((dataset) => !dataset.hidden);
|
||||
if (nonHiddenDatasets.length <= 1) return false;
|
||||
|
||||
if (!skipNull) return nonHiddenDatasets.length > 1; //color by will skip nulls
|
||||
|
||||
// Collect unique yAxisKeys from non-hidden datasets
|
||||
const uniqueYAxisKeys = new Set<string>();
|
||||
|
||||
nonHiddenDatasets.forEach((dataset) => {
|
||||
if (dataset.yAxisKey) {
|
||||
uniqueYAxisKeys.add(dataset.yAxisKey as string);
|
||||
}
|
||||
});
|
||||
|
||||
return !(uniqueYAxisKeys.size > 1);
|
||||
}, [datasets]);
|
||||
|
||||
const tooltipItems: ITooltipItem[] = useMemo(() => {
|
||||
if (isBar || isLine || isComboChart) {
|
||||
const hasMultipleShownDatasets = datasets.filter((dataset) => !dataset.hidden).length > 1;
|
||||
|
||||
return barAndLineTooltipHelper(
|
||||
dataPoints,
|
||||
chart,
|
||||
columnLabelFormats,
|
||||
keyToUsePercentage,
|
||||
hasMultipleShownDatasets,
|
||||
percentageMode
|
||||
percentageMode,
|
||||
skipNull
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -55,7 +55,8 @@ describe('barAndLineTooltipHelper', () => {
|
|||
mockColumnLabelFormats,
|
||||
[],
|
||||
false, // hasMultipleShownDatasets = false
|
||||
undefined
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
|
||||
// Assert
|
||||
|
@ -100,7 +101,8 @@ describe('barAndLineTooltipHelper', () => {
|
|||
mockColumnLabelFormats,
|
||||
[],
|
||||
true, // hasMultipleShownDatasets = true
|
||||
undefined
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
|
||||
// Assert
|
||||
|
@ -132,7 +134,8 @@ describe('barAndLineTooltipHelper', () => {
|
|||
mockColumnLabelFormats,
|
||||
[],
|
||||
false,
|
||||
undefined
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
|
||||
// Assert
|
||||
|
@ -166,7 +169,8 @@ describe('barAndLineTooltipHelper', () => {
|
|||
mockColumnLabelFormats,
|
||||
[],
|
||||
false,
|
||||
undefined
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
|
||||
// Assert
|
||||
|
@ -198,7 +202,8 @@ describe('barAndLineTooltipHelper', () => {
|
|||
mockColumnLabelFormats,
|
||||
[],
|
||||
false,
|
||||
undefined
|
||||
undefined,
|
||||
false
|
||||
);
|
||||
|
||||
// Assert
|
||||
|
@ -240,7 +245,8 @@ describe('barAndLineTooltipHelper', () => {
|
|||
mockColumnLabelFormats,
|
||||
['percentage_metric'],
|
||||
true,
|
||||
'stacked' // percentageMode = 'stacked'
|
||||
'stacked', // percentageMode = 'stacked'
|
||||
false
|
||||
);
|
||||
|
||||
// Assert
|
||||
|
@ -258,4 +264,60 @@ describe('barAndLineTooltipHelper', () => {
|
|||
// Verify getPercentage was called for both items
|
||||
expect(mockGetPercentage).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should filter out data points with null raw values when skipNull is true', () => {
|
||||
// Arrange
|
||||
const mockDataPoints: TooltipItem<keyof ChartTypeRegistry>[] = [
|
||||
{
|
||||
dataset: {
|
||||
label: 'Revenue Dataset',
|
||||
backgroundColor: '#ff0000',
|
||||
tooltipData: [[{ key: 'revenue', value: 1000 }]],
|
||||
yAxisKey: 'revenue',
|
||||
},
|
||||
dataIndex: 0,
|
||||
datasetIndex: 0,
|
||||
raw: 1000, // Valid data point
|
||||
} as unknown as TooltipItem<keyof ChartTypeRegistry>,
|
||||
{
|
||||
dataset: {
|
||||
label: 'Null Dataset',
|
||||
backgroundColor: '#00ff00',
|
||||
tooltipData: [[{ key: 'null_metric', value: null }]],
|
||||
yAxisKey: 'null_metric',
|
||||
},
|
||||
dataIndex: 1,
|
||||
datasetIndex: 1,
|
||||
raw: null, // This should be filtered out
|
||||
} as unknown as TooltipItem<keyof ChartTypeRegistry>,
|
||||
{
|
||||
dataset: {
|
||||
label: 'Another Dataset',
|
||||
backgroundColor: '#0000ff',
|
||||
tooltipData: [[{ key: 'another', value: 500 }]],
|
||||
yAxisKey: 'another',
|
||||
},
|
||||
dataIndex: 2,
|
||||
datasetIndex: 2,
|
||||
raw: 500, // Valid data point
|
||||
} as unknown as TooltipItem<keyof ChartTypeRegistry>,
|
||||
];
|
||||
|
||||
mockFormatLabel.mockReturnValue('formatted');
|
||||
mockGetPercentage.mockReturnValue('25%');
|
||||
|
||||
// Act
|
||||
const result = barAndLineTooltipHelper(
|
||||
mockDataPoints,
|
||||
mockChart,
|
||||
mockColumnLabelFormats,
|
||||
[],
|
||||
false,
|
||||
undefined,
|
||||
true // skipNull = true
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(result).toHaveLength(1); // Should only return 2 items (null one filtered out)
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,12 +10,17 @@ export const barAndLineTooltipHelper = (
|
|||
columnLabelFormats: NonNullable<ChartConfigProps['columnLabelFormats']>,
|
||||
keyToUsePercentage: string[],
|
||||
hasMultipleShownDatasets: boolean,
|
||||
percentageMode: undefined | 'stacked'
|
||||
percentageMode: undefined | 'stacked',
|
||||
skipNull: boolean
|
||||
): ITooltipItem[] => {
|
||||
if (percentageMode) {
|
||||
dataPoints.reverse(); //we do this because the data points are in reverse order and it looks better
|
||||
}
|
||||
|
||||
if (skipNull) {
|
||||
dataPoints = dataPoints.filter((dataPoint) => dataPoint.raw !== null);
|
||||
}
|
||||
|
||||
const tooltipItems = dataPoints.flatMap<ITooltipItem>((dataPoint) => {
|
||||
const tooltipDataset = dataPoint.dataset;
|
||||
const dataPointDataIndex = dataPoint.dataIndex;
|
||||
|
|
Loading…
Reference in New Issue