mirror of
https://github.com/supabase/supabase.git
synced 2026-10-05 09:25:06 +03:00
Fixes: #47377 ## What is the current behavior? Enabling **Cumulative** on a results chart concatenates Y-axis values instead of summing them whenever the column is a `bigint`, `numeric`, `money`, or `count(*)` aggregate — which Postgres returns as JSON strings. For per-row values `10, 20, 30` the chart plots `10, 1020, 102030`. `getCumulativeResults` ran `(prev[yKey] || 0) + row[yKey]` on raw result rows. The Y-axis selector explicitly allows numeric-string columns, so this is a common, fully-supported path (e.g. any `count(*) ... group by`). ## What is the new behavior? Both operands are coerced with `Number()` before the addition, keeping the existing `|| 0` fallback for null/undefined/non-numeric values. The series now sums correctly: `10, 30, 60`. The cumulative logic was previously duplicated in `ChartConfig.tsx` and `QueryBlock.utils.ts` (which is how this bug slipped in twice). It is now a single shared, tested helper: `getCumulativeResults` lives in `QueryBlock.utils.ts`, and `ChartConfig.tsx` imports it instead of re-declaring its own copy. The shared helper's `ChartConfig` type import is `import type` to avoid a runtime circular dependency, and its signature accepts `readonly` rows so both call sites type-check. ## Additional context - Added regression tests for numeric-string inputs and for null/undefined/non-numeric fallback to `0`. The existing tests only covered literal `number` inputs, never the string form Postgres actually returns. - Verified the new tests fail against the old code (`y: '010'`, `'05undefined'`) and pass with the fix. Full `QueryBlock.utils.test.ts` suite: 18 passing. No migrations, no API changes, no infra changes. <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Bug Fixes** * Fixed cumulative chart calculations so numeric values are always added correctly, even when results arrive as strings. * Improved handling of empty or non-numeric values in cumulative totals so they are treated as zero instead of breaking the sum. * **Tests** * Added coverage for cumulative result calculations with numeric strings and missing values. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
69 lines
2.2 KiB
TypeScript
69 lines
2.2 KiB
TypeScript
import type { ChartConfig } from '@/components/interfaces/SQLEditor/UtilityPanel/ChartConfig'
|
|
|
|
export const checkHasNonPositiveValues = (data: Record<string, unknown>[], key: string): boolean =>
|
|
data.some((row) => (row[key] as number) <= 0)
|
|
|
|
export const formatYAxisTick = (value: number): string => {
|
|
if (Math.abs(value) >= 1_000_000) {
|
|
const n = value / 1_000_000
|
|
return `${Number.isInteger(n) ? n : n.toFixed(1)}M`
|
|
}
|
|
if (Math.abs(value) >= 1_000) {
|
|
const n = value / 1_000
|
|
return `${Number.isInteger(n) ? n : n.toFixed(1)}K`
|
|
}
|
|
if (value !== 0 && Math.abs(value) < 1) {
|
|
return parseFloat(value.toFixed(2)).toString()
|
|
}
|
|
if (!Number.isInteger(value)) {
|
|
return parseFloat(value.toFixed(1)).toString()
|
|
}
|
|
return String(value)
|
|
}
|
|
|
|
export const computeYAxisWidth = (
|
|
data: Record<string, unknown>[],
|
|
key: string,
|
|
{
|
|
isLogScale = false,
|
|
isPercentage = false,
|
|
}: { isLogScale?: boolean; isPercentage?: boolean } = {}
|
|
): number => {
|
|
if (isLogScale) return 52
|
|
if (isPercentage) return Math.max(36, (3 + 1) * 8) // max tick is "100"
|
|
|
|
const maxMagnitude = data.reduce((max, d) => {
|
|
const magnitude = Math.abs(Number(d[key]) || 0)
|
|
return magnitude > max ? magnitude : max
|
|
}, 0)
|
|
return Math.max(36, (formatYAxisTick(maxMagnitude).length + 1) * 8)
|
|
}
|
|
|
|
export const formatLogTick = (value: number): string => {
|
|
if (value >= 1_000_000)
|
|
return `${(value / 1_000_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}M`
|
|
if (value >= 1_000)
|
|
return `${(value / 1_000).toLocaleString(undefined, { maximumFractionDigits: 1 })}k`
|
|
return value.toLocaleString()
|
|
}
|
|
|
|
export const getCumulativeResults = (results: { rows: readonly any[] }, config: ChartConfig) => {
|
|
if (!results?.rows?.length) {
|
|
return []
|
|
}
|
|
|
|
const cumulativeResults = results.rows.reduce((acc, row) => {
|
|
const prev = acc[acc.length - 1] || {}
|
|
// Coerce to Number before adding: Postgres returns `bigint`, `numeric`,
|
|
// `money` and `count(*)` columns as strings, so a bare `+` would
|
|
// concatenate (e.g. "10" + "20" -> "1020") instead of summing.
|
|
const next = {
|
|
...row,
|
|
[config.yKey]: (Number(prev[config.yKey]) || 0) + (Number(row[config.yKey]) || 0),
|
|
}
|
|
return [...acc, next]
|
|
}, [])
|
|
|
|
return cumulativeResults
|
|
}
|