mirror of
https://github.com/supabase/supabase.git
synced 2026-05-08 01:40:13 -04:00
bf16d7f613
## Problem The Report Blocks section (custom dashboards) has four visual and UX bugs: tooltip content overflows its container, Y-axis labels with 5+ digits get clipped (e.g. `10000` renders as `0000`), action buttons become unreachable when a block title is long, and scrolling inside a scrollable block also scrolls the parent page. ## Fix - Remove the fixed `w-[200px]` class from `ChartTooltipContent` in `ChartBlock` so tooltips auto-size to their content instead of overflowing. - Compute a dynamic Y-axis width in `ChartBlock` based on the string length of the maximum data value, replacing the `undefined` default that caused clipping. - Add `min-w-0` to the label container and `shrink-0` to the actions container in `ReportBlockContainer` so the truncation works correctly and action buttons are never pushed off-screen. - Add `overscroll-contain` to the scrollable SQL code and results table divs in `QueryBlock` to stop scroll events from propagating to the page. ## How to test - Navigate to a custom Report with multiple blocks - Hover over a chart bar on a block with a long metric name. The tooltip should be fully visible with no text overflow. - Find or create a block whose Y-axis values exceed 9999 (e.g. disk IOPS). The full number should appear on the Y-axis without any leading digits being clipped. - Use a block on a read replica so the label appends "of replica", making it long. The chart-type toggle, log scale toggle, and remove buttons should all remain visible and clickable. - Add a SQL snippet block that returns a large table of results. Scroll within the results table. The page should not scroll while the inner table is scrolling. ## Before <img width="1166" height="680" alt="CleanShot 2026-04-07 at 15 36 45@2x" src="https://github.com/user-attachments/assets/8e7bd3c9-8319-47c9-b2d9-b194d2803809" /> ## After <img width="1166" height="680" alt="CleanShot 2026-04-07 at 15 36 15@2x" src="https://github.com/user-attachments/assets/6ca5873a-cd09-4001-9cd0-932c12b6536e" /> <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **Style** * More consistent Y-axis sizing with dynamic widths for better label fit. * Improved Y-axis number formatting (K/M suffixes, sensible decimals) for clearer tick labels. * Simplified, more flexible chart tooltips (min-width applied; removed fixed widths). * Tighter report header layout so labels truncate predictably and actions keep their size. * Added overscroll containment to query results and SQL view to reduce unwanted scrolling. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
93 lines
3.1 KiB
TypeScript
93 lines
3.1 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
|
|
import { computeYAxisWidth, formatYAxisTick } from './QueryBlock.utils'
|
|
|
|
describe('formatYAxisTick', () => {
|
|
it('returns integers as-is when below 1000', () => {
|
|
expect(formatYAxisTick(0)).toBe('0')
|
|
expect(formatYAxisTick(1)).toBe('1')
|
|
expect(formatYAxisTick(999)).toBe('999')
|
|
})
|
|
|
|
it('abbreviates thousands with K', () => {
|
|
expect(formatYAxisTick(1_000)).toBe('1K')
|
|
expect(formatYAxisTick(5_000)).toBe('5K')
|
|
expect(formatYAxisTick(999_000)).toBe('999K')
|
|
})
|
|
|
|
it('rounds thousands to one decimal place', () => {
|
|
expect(formatYAxisTick(1_500)).toBe('1.5K')
|
|
expect(formatYAxisTick(55_300)).toBe('55.3K')
|
|
expect(formatYAxisTick(1_234)).toBe('1.2K')
|
|
})
|
|
|
|
it('abbreviates millions with M', () => {
|
|
expect(formatYAxisTick(1_000_000)).toBe('1M')
|
|
expect(formatYAxisTick(2_000_000)).toBe('2M')
|
|
})
|
|
|
|
it('rounds millions to one decimal place', () => {
|
|
expect(formatYAxisTick(1_500_000)).toBe('1.5M')
|
|
expect(formatYAxisTick(3_208_914)).toBe('3.2M')
|
|
})
|
|
|
|
it('handles values just below the million threshold', () => {
|
|
expect(formatYAxisTick(999_900)).toBe('999.9K')
|
|
})
|
|
|
|
it('handles negative values', () => {
|
|
expect(formatYAxisTick(-1_000)).toBe('-1K')
|
|
expect(formatYAxisTick(-1_500)).toBe('-1.5K')
|
|
expect(formatYAxisTick(-1_000_000)).toBe('-1M')
|
|
expect(formatYAxisTick(-999)).toBe('-999')
|
|
})
|
|
|
|
it('rounds small decimals to 2 places', () => {
|
|
expect(formatYAxisTick(0.456)).toBe('0.46')
|
|
expect(formatYAxisTick(0.1)).toBe('0.1')
|
|
expect(formatYAxisTick(-0.123)).toBe('-0.12')
|
|
})
|
|
|
|
it('rounds non-integer values between 1 and 1000 to 1 decimal place', () => {
|
|
expect(formatYAxisTick(1.25)).toBe('1.3')
|
|
expect(formatYAxisTick(99.9)).toBe('99.9')
|
|
expect(formatYAxisTick(5.0)).toBe('5')
|
|
})
|
|
})
|
|
|
|
describe('computeYAxisWidth', () => {
|
|
const row = (v: number) => ({ val: v })
|
|
|
|
it('returns 52 for log scale regardless of data', () => {
|
|
expect(computeYAxisWidth([row(1_000_000)], 'val', { isLogScale: true })).toBe(52)
|
|
})
|
|
|
|
it('returns a fixed width for percentage data', () => {
|
|
const width = computeYAxisWidth([row(99)], 'val', { isPercentage: true })
|
|
// "100" is the longest tick → (3+1)*8 = 32, floor at 36
|
|
expect(width).toBe(36)
|
|
})
|
|
|
|
it('returns minimum 36 for small values', () => {
|
|
expect(computeYAxisWidth([row(5)], 'val')).toBe(36)
|
|
expect(computeYAxisWidth([], 'val')).toBe(36)
|
|
})
|
|
|
|
it('widens for large values', () => {
|
|
// formatYAxisTick(55_300) = "55.3K" (5 chars) → (5+1)*8 = 48
|
|
expect(computeYAxisWidth([row(55_300)], 'val')).toBe(48)
|
|
})
|
|
|
|
it('uses absolute magnitude so negative data is handled correctly', () => {
|
|
const negWidth = computeYAxisWidth([row(-55_300)], 'val')
|
|
const posWidth = computeYAxisWidth([row(55_300)], 'val')
|
|
expect(negWidth).toBe(posWidth)
|
|
})
|
|
|
|
it('picks the largest magnitude across all rows', () => {
|
|
const data = [row(100), row(5_000), row(200)]
|
|
// max is 5000 → "5K" (2 chars) → (2+1)*8 = 24, floor at 36
|
|
expect(computeYAxisWidth(data, 'val')).toBe(36)
|
|
})
|
|
})
|