remove unused stacking properties

This commit is contained in:
Jordi Enric committed 2026-01-15 14:55:55 +07:00
1 parent 0d57baada0
commit 81a694d6bd
5 files changed
+5 -253

No files matched your search

@@ -36,7 +36,6 @@ import {
CustomLabel,
CustomTooltip,
MultiAttribute,
StackingMode,
} from './ComposedChart.utils'
import NoDataPlaceholder from './NoDataPlaceholder'
import { ChartHighlight } from './useChartHighlight'
@@ -73,9 +72,6 @@ export interface ComposedChartProps<D = Datum> extends CommonChartProps<D> {
sql?: string
highlightActions?: ChartHighlightAction[]
showNewBadge?: boolean
// Controls how stacked values are displayed: 'normal' shows raw values, 'percentage' normalizes to 100%
// This is necessary for multi-core CPU metrics where values can exceed 100%, making them comparable across different core counts
stackingMode?: StackingMode
}
interface CustomizedDotProps {
@@ -135,7 +131,6 @@ export function ComposedChart({
highlightActions,
titleTooltip,
showNewBadge,
stackingMode = 'normal',
}: ComposedChartProps) {
const { resolvedTheme } = useTheme()
const { hoveredIndex, syncTooltip, setHover, clearHover } = useChartHoverState(
@@ -272,8 +267,6 @@ export function ComposedChart({
chartHighlight?.coordinates.right &&
chartHighlight?.coordinates.left !== chartHighlight?.coordinates.right
const isPercentageStacking = stackingMode === 'percentage'
const initialChartData =
data && !!data[0]
? Object.entries(data[0])
@@ -491,9 +484,7 @@ export function ComposedChart({
hide={hideYAxis}
axisLine={{ stroke: CHART_COLORS.AXIS }}
tickLine={{ stroke: CHART_COLORS.AXIS }}
domain={
isPercentageStacking || (isPercentage && !showMaxValue) ? yDomain : ['auto', 'auto']
}
domain={isPercentage && !showMaxValue ? yDomain : ['auto', 'auto']}
key={yAxisKey}
/>
<XAxis
@@ -1,11 +1,5 @@
import { describe, expect, it } from 'vitest'
import {
convertDataPointToPayload,
normalizeToPercentageStacking,
selectStackedAttributeKeys,
transformDataForStacking,
type MultiAttribute,
} from './ComposedChart.utils'
import { convertDataPointToPayload, type MultiAttribute } from './ComposedChart.utils'
describe('convertDataPointToPayload', () => {
it('should convert data point to payload format', () => {
@@ -141,154 +135,3 @@ describe('convertDataPointToPayload', () => {
])
})
})
describe('normalizeToPercentageStacking', () => {
it('keeps values unchanged when total is exactly 100%', () => {
const data = [
{ timestamp: 1, cpu_system: 20, cpu_user: 30, cpu_other: 50 },
{ timestamp: 2, cpu_system: 10, cpu_user: 40, cpu_other: 50 },
]
const attributeKeys = ['cpu_system', 'cpu_user', 'cpu_other']
const result: typeof data = normalizeToPercentageStacking(data, attributeKeys)
expect(result[0].cpu_system).toBe(20)
expect(result[0].cpu_user).toBe(30)
expect(result[0].cpu_other).toBe(50)
expect(result[1].cpu_system).toBe(10)
expect(result[1].cpu_user).toBe(40)
expect(result[1].cpu_other).toBe(50)
})
it('keeps values unchanged when total is below 100%', () => {
const data = [{ timestamp: 1, cpu_system: 5, cpu_user: 10, cpu_other: 35 }]
const attributeKeys = ['cpu_system', 'cpu_user', 'cpu_other']
const result: typeof data = normalizeToPercentageStacking(data, attributeKeys)
expect(result[0].cpu_system).toBe(5)
expect(result[0].cpu_user).toBe(10)
expect(result[0].cpu_other).toBe(35)
})
it('normalizes values exceeding 100% (multi-core CPU)', () => {
const data = [{ timestamp: 1, cpu_system: 40, cpu_user: 60, cpu_other: 100 }]
const attributeKeys = ['cpu_system', 'cpu_user', 'cpu_other']
const result: typeof data = normalizeToPercentageStacking(data, attributeKeys)
expect(result[0].cpu_system).toBe(20)
expect(result[0].cpu_user).toBe(30)
expect(result[0].cpu_other).toBe(50)
expect(result[0].cpu_system + result[0].cpu_user + result[0].cpu_other).toBe(100)
})
it('normalizes 2-core CPU at 150% to 100%', () => {
const data = [{ timestamp: 1, cpu_system: 30, cpu_user: 60, cpu_iowait: 60 }]
const attributeKeys = ['cpu_system', 'cpu_user', 'cpu_iowait']
const result: typeof data = normalizeToPercentageStacking(data, attributeKeys)
expect(result[0].cpu_system).toBe(20)
expect(result[0].cpu_user).toBe(40)
expect(result[0].cpu_iowait).toBe(40)
expect(result[0].cpu_system + result[0].cpu_user + result[0].cpu_iowait).toBe(100)
})
it('preserves non-attribute fields unchanged', () => {
const data = [
{ timestamp: 1704067200000, period_start: '2024-01-01', cpu_system: 80, cpu_user: 120 },
]
const attributeKeys = ['cpu_system', 'cpu_user']
const result: typeof data = normalizeToPercentageStacking(data, attributeKeys)
expect(result[0].timestamp).toBe(1704067200000)
expect(result[0].period_start).toBe('2024-01-01')
})
it('handles data points with zero total gracefully', () => {
const data = [{ timestamp: 1, cpu_system: 0, cpu_user: 0, cpu_other: 0 }]
const attributeKeys = ['cpu_system', 'cpu_user', 'cpu_other']
const result = normalizeToPercentageStacking(data, attributeKeys)
expect(result[0].cpu_system).toBe(0)
expect(result[0].cpu_user).toBe(0)
expect(result[0].cpu_other).toBe(0)
})
it('handles empty data array', () => {
const data: Array<Record<string, number>> = []
const attributeKeys = ['cpu_system', 'cpu_user']
const result: typeof data = normalizeToPercentageStacking(data, attributeKeys)
expect(result).toEqual([])
})
it('keeps values unchanged when single attribute is below 100%', () => {
const data = [{ timestamp: 1, cpu_system: 60 }]
const attributeKeys = ['cpu_system', 'cpu_user']
const result = normalizeToPercentageStacking(data, attributeKeys)
expect(result[0].cpu_system).toBe(60)
expect((result[0] as Record<string, unknown>).cpu_user).toBeUndefined()
})
it('maintains proportions when normalizing values over 100%', () => {
const data = [{ timestamp: 1, a: 100, b: 200, c: 300 }]
const attributeKeys = ['a', 'b', 'c']
const result: typeof data = normalizeToPercentageStacking(data, attributeKeys)
const ratioAB = result[0].a / result[0].b
const ratioBC = result[0].b / result[0].c
expect(ratioAB).toBeCloseTo(0.5)
expect(ratioBC).toBeCloseTo(2 / 3)
expect(result[0].a + result[0].b + result[0].c).toBe(100)
})
})
describe('selectStackedAttributeKeys', () => {
it('filters out reference lines, max values, disabled and special attributes', () => {
const attributes: MultiAttribute[] = [
{ attribute: 'a', enabled: true },
{ attribute: 'b', enabled: false },
{ attribute: 'c', provider: 'reference-line' },
{ attribute: 'd', isMaxValue: true },
{ attribute: 'rest' },
{ attribute: 'e' },
]
const keys = selectStackedAttributeKeys(attributes)
expect(keys).toEqual(['a', 'e'])
})
})
describe('transformDataForStacking', () => {
it('returns input when stackingMode is normal', () => {
const data = [{ a: 1, b: 2 }]
const attributes: MultiAttribute[] = [{ attribute: 'a' }, { attribute: 'b' }]
const result = transformDataForStacking(data, attributes, 'normal')
expect(result).toEqual(data)
})
it('normalizes data when stackingMode is percentage', () => {
const data = [{ timestamp: 1, a: 100, b: 200 }]
const attributes: MultiAttribute[] = [{ attribute: 'a' }, { attribute: 'b' }]
const result = transformDataForStacking(data, attributes, 'percentage')!
expect(result[0].a + result[0].b).toBe(100)
})
it('handles undefined or empty data gracefully', () => {
const attributes: MultiAttribute[] = [{ attribute: 'a' }]
expect(transformDataForStacking(undefined, attributes, 'percentage')).toBeUndefined()
expect(transformDataForStacking([], attributes, 'percentage')).toEqual([])
})
})
@@ -7,8 +7,6 @@ import { cn, Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from 'ui
import { CHART_COLORS, DateTimeFormats } from './Charts.constants'
import { formatPercentage, numberFormatter } from './Charts.utils'
export type StackingMode = 'normal' | 'percentage'
export interface ReportAttributes {
id?: string
titleTooltip?: string
@@ -33,7 +31,6 @@ export interface ReportAttributes {
tickFormatter?: (value: any) => string
}
hideHighlightedValue?: boolean
stackingMode?: StackingMode
}
export type Provider = 'infra-monitoring' | 'daily-stats' | 'mock' | 'reference-line' | 'logs'
@@ -146,76 +143,6 @@ export const convertDataPointToPayload = (
)
}
/**
* Normalize data points to 100% stacking mode.
* Only normalizes when total exceeds 100% (e.g., multi-core CPU metrics).
* Values below or equal to 100% are kept as-is to show actual utilization.
*/
export const normalizeToPercentageStacking = <T extends Record<string, unknown>>(
data: T[],
attributeKeys: string[]
): T[] => {
return data.map((point) => {
const total = attributeKeys.reduce((sum, attr) => {
const value = point[attr]
return sum + (typeof value === 'number' ? value : 0)
}, 0)
if (total <= 100) {
return point
}
const normalized = { ...point }
const lastAttribute = attributeKeys[attributeKeys.length - 1]
let accumulated = 0
attributeKeys.forEach((attr, index) => {
const value = point[attr]
if (typeof value !== 'number') return
if (attr === lastAttribute && index === attributeKeys.length - 1) {
const remainder = 100 - accumulated
;(normalized as Record<string, unknown>)[attr] = remainder
return
}
const normalizedValue = (value / total) * 100
accumulated += normalizedValue
;(normalized as Record<string, unknown>)[attr] = normalizedValue
})
return normalized
})
}
/**
* Select attribute keys to be included in stacking normalization.
* Filters out reference lines, max value lines, disabled and special attributes.
*/
export const selectStackedAttributeKeys = (attributes: MultiAttribute[]): string[] => {
return attributes
.filter((attr) => attr && attr.enabled !== false)
.filter((attr) => attr.provider !== 'reference-line')
.filter((attr) => !attr.isMaxValue)
.filter((attr) => attr.attribute !== 'rest')
.map((attr) => attr.attribute)
}
/**
* Transform chart data according to stacking mode, applying percentage normalization when needed.
*/
export const transformDataForStacking = <T extends Record<string, unknown>>(
data: T[] | undefined,
attributes: MultiAttribute[],
stackingMode?: StackingMode
): T[] | undefined => {
if (!data || data.length === 0) return data
if (stackingMode !== 'percentage') return data
const keys = selectStackedAttributeKeys(attributes)
if (keys.length === 0) return data
return normalizeToPercentageStacking(data, keys)
}
/**
* Calculate the total aggregate of the chart values
* by summing the values of the attributes
@@ -18,7 +18,7 @@ import { useChartHighlight } from './useChartHighlight'
import dayjs from 'dayjs'
import type { UpdateDateRange } from 'pages/project/[ref]/observability/database'
import type { ChartData } from './Charts.types'
import { MultiAttribute, StackingMode, transformDataForStacking } from './ComposedChart.utils'
import { MultiAttribute } from './ComposedChart.utils'
export interface ComposedChartHandlerProps {
id?: string
@@ -45,7 +45,6 @@ export interface ComposedChartHandlerProps {
docsUrl?: string
hide?: boolean
syncId?: string
stackingMode?: StackingMode
hideHighlightedValue?: boolean
}
@@ -207,18 +206,11 @@ const ComposedChartHandler = ({
return combined as DataPoint[]
}, [data, attributeQueries, attributes])
// Apply pre-render transformations (e.g., percentage normalization)
const processedData = useMemo(() => {
const dataPoints = Array.isArray(combinedData)
return Array.isArray(combinedData)
? combinedData.map((point) => ({ ...point }))
: combinedData?.data?.map((point) => ({ ...point }))
return transformDataForStacking<Record<string, unknown>>(
dataPoints,
attributes,
otherProps.stackingMode
)
}, [combinedData, attributes, otherProps.stackingMode])
}, [combinedData])
const loading = isLoading || attributeQueries.some((query: any) => query.isLoading)
@@ -74,7 +74,6 @@ export const getReportAttributesV2: (
showLegend: true,
showMaxValue: true,
showGrid: true,
stackingMode: 'percentage',
YAxisProps: {
width: 45,
tickFormatter: (value: any) => {