diff --git a/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useOptions.tsx b/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useOptions.tsx index 3efb06c95..2fdc1db82 100644 --- a/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useOptions.tsx +++ b/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useOptions.tsx @@ -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['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, diff --git a/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/BusterChartJSTooltip.tsx b/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/BusterChartJSTooltip.tsx index 17c085644..9f7ded9ca 100644 --- a/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/BusterChartJSTooltip.tsx +++ b/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/BusterChartJSTooltip.tsx @@ -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(); + + 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 ); } diff --git a/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/barAndLineTooltipHelper.test.ts b/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/barAndLineTooltipHelper.test.ts index d697afdce..2833abd7b 100644 --- a/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/barAndLineTooltipHelper.test.ts +++ b/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/barAndLineTooltipHelper.test.ts @@ -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[] = [ + { + 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, + { + 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, + { + 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, + ]; + + 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) + }); }); diff --git a/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/barAndLineTooltipHelper.ts b/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/barAndLineTooltipHelper.ts index 4c6f41ffa..4314e1f3f 100644 --- a/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/barAndLineTooltipHelper.ts +++ b/apps/web/src/components/ui/charts/BusterChartJS/hooks/useOptions/useTooltipOptions.ts/barAndLineTooltipHelper.ts @@ -10,12 +10,17 @@ export const barAndLineTooltipHelper = ( columnLabelFormats: NonNullable, 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((dataPoint) => { const tooltipDataset = dataPoint.dataset; const dataPointDataIndex = dataPoint.dataIndex;