@@ -3,7 +3,8 @@ import { memo, useMemo } from "react";
33import type { ChartConfig } from "~/components/primitives/charts/Chart" ;
44import { Chart } from "~/components/primitives/charts/ChartCompound" ;
55import { Paragraph } from "../primitives/Paragraph" ;
6- import { AggregationType , ChartConfiguration } from "../metrics/QueryWidget" ;
6+ import type { AggregationType , ChartConfiguration } from "../metrics/QueryWidget" ;
7+ import { aggregateValues } from "../primitives/charts/aggregation" ;
78import { getRunStatusHexColor } from "~/components/runs/v3/TaskRunStatus" ;
89import { getSeriesColor } from "./chartColors" ;
910
@@ -243,17 +244,18 @@ function fillTimeGaps(
243244 }
244245 filledData . push ( point ) ;
245246 } else {
246- // Create a zero-filled data point
247- const zeroPoint : Record < string , unknown > = {
247+ // Create a null-filled data point so gaps appear in line/bar charts
248+ // and legend aggregations (avg/min/max) skip these slots
249+ const gapPoint : Record < string , unknown > = {
248250 [ xDataKey ] : t ,
249251 __rawDate : new Date ( t ) ,
250252 __granularity : granularity ,
251253 __originalX : new Date ( t ) . toISOString ( ) ,
252254 } ;
253255 for ( const s of series ) {
254- zeroPoint [ s ] = 0 ;
256+ gapPoint [ s ] = null ;
255257 }
256- filledData . push ( zeroPoint ) ;
258+ filledData . push ( gapPoint ) ;
257259 }
258260 }
259261
@@ -671,25 +673,6 @@ function toNumber(value: unknown): number {
671673 return 0 ;
672674}
673675
674- /**
675- * Aggregate an array of numbers using the specified aggregation function
676- */
677- function aggregateValues ( values : number [ ] , aggregation : AggregationType ) : number {
678- if ( values . length === 0 ) return 0 ;
679- switch ( aggregation ) {
680- case "sum" :
681- return values . reduce ( ( a , b ) => a + b , 0 ) ;
682- case "avg" :
683- return values . reduce ( ( a , b ) => a + b , 0 ) / values . length ;
684- case "count" :
685- return values . length ;
686- case "min" :
687- return Math . min ( ...values ) ;
688- case "max" :
689- return Math . max ( ...values ) ;
690- }
691- }
692-
693676/**
694677 * Sort data array by a specified column
695678 */
@@ -775,6 +758,24 @@ export const QueryResultsChart = memo(function QueryResultsChart({
775758 return sortData ( unsortedData , sortByColumn , sortDirection , xDataKey ) ;
776759 } , [ unsortedData , sortByColumn , sortDirection , isDateBased , xDataKey ] ) ;
777760
761+ // Sort series by descending total sum so largest appears at bottom of
762+ // stacked charts and first in the legend
763+ const sortedSeries = useMemo ( ( ) => {
764+ if ( series . length <= 1 ) return series ;
765+ const totals = new Map < string , number > ( ) ;
766+ for ( const s of series ) {
767+ let total = 0 ;
768+ for ( const point of data ) {
769+ const val = point [ s ] ;
770+ if ( typeof val === "number" && isFinite ( val ) ) {
771+ total += Math . abs ( val ) ;
772+ }
773+ }
774+ totals . set ( s , total ) ;
775+ }
776+ return [ ...series ] . sort ( ( a , b ) => ( totals . get ( b ) ?? 0 ) - ( totals . get ( a ) ?? 0 ) ) ;
777+ } , [ series , data ] ) ;
778+
778779 // Detect time granularity — use the full time range when available so tick
779780 // labels are appropriate for the period (e.g. "Jan 5" for a 7-day range
780781 // instead of just "16:00:00" when data is sparse)
@@ -809,15 +810,15 @@ export const QueryResultsChart = memo(function QueryResultsChart({
809810 // Build chart config for colors/labels
810811 const chartConfig = useMemo ( ( ) => {
811812 const cfg : ChartConfig = { } ;
812- series . forEach ( ( s , i ) => {
813+ sortedSeries . forEach ( ( s , i ) => {
813814 const statusColor = groupByIsRunStatus ? getRunStatusHexColor ( s ) : undefined ;
814815 cfg [ s ] = {
815816 label : s ,
816817 color : statusColor ?? config . seriesColors ?. [ s ] ?? getSeriesColor ( i ) ,
817818 } ;
818819 } ) ;
819820 return cfg ;
820- } , [ series , groupByIsRunStatus , config . seriesColors ] ) ;
821+ } , [ sortedSeries , groupByIsRunStatus , config . seriesColors ] ) ;
821822
822823 // Custom tooltip label formatter for better date display
823824 const tooltipLabelFormatter = useMemo ( ( ) => {
@@ -1002,18 +1003,19 @@ export const QueryResultsChart = memo(function QueryResultsChart({
10021003 domain : yAxisDomain ,
10031004 } ;
10041005
1005- const showLegend = series . length > 0 ;
1006+ const showLegend = sortedSeries . length > 0 ;
10061007
10071008 if ( chartType === "bar" ) {
10081009 return (
10091010 < Chart . Root
10101011 config = { chartConfig }
10111012 data = { data }
10121013 dataKey = { xDataKey }
1013- series = { series }
1014+ series = { sortedSeries }
10141015 labelFormatter = { legendLabelFormatter }
10151016 showLegend = { showLegend }
10161017 maxLegendItems = { fullLegend ? Infinity : 5 }
1018+ legendAggregation = { config . aggregation }
10171019 minHeight = "300px"
10181020 fillContainer
10191021 onViewAllLegendItems = { onViewAllLegendItems }
@@ -1036,10 +1038,11 @@ export const QueryResultsChart = memo(function QueryResultsChart({
10361038 config = { chartConfig }
10371039 data = { data }
10381040 dataKey = { xDataKey }
1039- series = { series }
1041+ series = { sortedSeries }
10401042 labelFormatter = { legendLabelFormatter }
10411043 showLegend = { showLegend }
10421044 maxLegendItems = { fullLegend ? Infinity : 5 }
1045+ legendAggregation = { config . aggregation }
10431046 minHeight = "300px"
10441047 fillContainer
10451048 onViewAllLegendItems = { onViewAllLegendItems }
@@ -1049,7 +1052,7 @@ export const QueryResultsChart = memo(function QueryResultsChart({
10491052 < Chart . Line
10501053 xAxisProps = { xAxisPropsForLine }
10511054 yAxisProps = { yAxisProps }
1052- stacked = { stacked && series . length > 1 }
1055+ stacked = { stacked && sortedSeries . length > 1 }
10531056 tooltipLabelFormatter = { tooltipLabelFormatter }
10541057 lineType = "linear"
10551058 />
0 commit comments