mirror of
https://github.com/supabase/supabase.git
synced 2026-10-12 12:55:06 +03:00
remove unused stacking properties
This commit is contained in:
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) => {
|
||||
|
||||
Reference in new issue
Block a user