fix PieChart rendering issues by giving default height.

This commit is contained in:
Francis Cao
2026-05-18 23:06:47 -07:00
parent c6b2364a41
commit a7a0d73dd7
6 changed files with 5 additions and 32 deletions
+1 -8
View File
@@ -13,7 +13,7 @@ ChartJS.defaults.font.family = 'Inter';
export interface ChartProps extends BoxProps { export interface ChartProps extends BoxProps {
type?: 'bar' | 'bubble' | 'doughnut' | 'pie' | 'line' | 'polarArea' | 'radar' | 'scatter'; type?: 'bar' | 'bubble' | 'doughnut' | 'pie' | 'line' | 'polarArea' | 'radar' | 'scatter';
chartData?: ChartData & { focusLabel?: string }; chartData?: ChartData<any, any, unknown> & { focusLabel?: string };
chartOptions?: ChartOptions; chartOptions?: ChartOptions;
updateMode?: UpdateMode; updateMode?: UpdateMode;
animationDuration?: number; animationDuration?: number;
@@ -66,8 +66,6 @@ export function Chart({
const handleLegendClick = (item: LegendItem) => { const handleLegendClick = (item: LegendItem) => {
if (onLegendClick && type === 'bar') { if (onLegendClick && type === 'bar') {
// Controlled mode: caller owns the hidden state. We report the click
// and let the parent push a new hiddenLabels set on the next render.
const { datasetIndex } = item; const { datasetIndex } = item;
const ds = chart.current.data.datasets[datasetIndex]; const ds = chart.current.data.datasets[datasetIndex];
onLegendClick(ds.label, !hiddenLabels?.has(ds.label)); onLegendClick(ds.label, !hiddenLabels?.has(ds.label));
@@ -124,16 +122,11 @@ export function Chart({
}); });
} }
// Re-apply caller-driven hidden flags after focusLabel handling so a
// dataset stays hidden across data changes (e.g. date-range switches)
// even though Chart.js regenerates dataset meta on every replace.
if (hiddenLabels) { if (hiddenLabels) {
chart.current.data.datasets.forEach((ds: { hidden: boolean; label: any }) => { chart.current.data.datasets.forEach((ds: { hidden: boolean; label: any }) => {
if (hiddenLabels.has(ds.label)) { if (hiddenLabels.has(ds.label)) {
ds.hidden = true; ds.hidden = true;
} else if (!chartData.focusLabel) { } else if (!chartData.focusLabel) {
// Explicitly reset so un-hiding a label is always reflected,
// regardless of whether the focusLabel pass ran above.
ds.hidden = false; ds.hidden = false;
} }
}); });
+2 -2
View File
@@ -6,7 +6,7 @@ export interface PieChartProps extends ChartProps {
type?: 'doughnut' | 'pie'; type?: 'doughnut' | 'pie';
} }
export function PieChart({ type = 'pie', ...props }: PieChartProps) { export function PieChart({ type = 'pie', height = '300px', ...props }: PieChartProps) {
const [tooltip, setTooltip] = useState(null); const [tooltip, setTooltip] = useState(null);
const handleTooltip = ({ tooltip }) => { const handleTooltip = ({ tooltip }) => {
@@ -24,7 +24,7 @@ export function PieChart({ type = 'pie', ...props }: PieChartProps) {
return ( return (
<> <>
<Chart {...props} type={type} onTooltip={handleTooltip} /> <Chart {...props} type={type} height={height} onTooltip={handleTooltip} />
{tooltip && <ChartTooltip {...tooltip} />} {tooltip && <ChartTooltip {...tooltip} />}
</> </>
); );
+2 -7
View File
@@ -58,16 +58,11 @@ export function DataGrid({
const showPager = allowPaging && data && data.count > 0; const showPager = allowPaging && data && data.count > 0;
const { isMobile } = useMobile(); const { isMobile } = useMobile();
const [userDisplayMode, setUserDisplayMode] = useState<DisplayMode | null>(() => { const [userDisplayMode, setUserDisplayMode] = useState<DisplayMode | null>(() => {
// localStorage can hold anything (extensions, manual edits, schema drift),
// so accept only the two values we know how to render and otherwise fall
// back to the useMobile-driven default.
const stored = getItem(DISPLAY_MODE_STORAGE_KEY); const stored = getItem(DISPLAY_MODE_STORAGE_KEY);
return stored === 'table' || stored === 'cards' ? stored : null; return stored === 'table' || stored === 'cards' ? stored : null;
}); });
// Effective mode: explicit user choice wins, otherwise fall back to the const displayMode: DisplayMode | undefined = isMobile ? 'cards' : userDisplayMode ?? undefined;
// mobile-driven default (cards on small viewports, table elsewhere).
const displayMode: DisplayMode | undefined = userDisplayMode ?? (isMobile ? 'cards' : undefined);
const handleToggleDisplayMode = () => { const handleToggleDisplayMode = () => {
const next: DisplayMode = displayMode === 'cards' ? 'table' : 'cards'; const next: DisplayMode = displayMode === 'cards' ? 'table' : 'cards';
@@ -116,7 +111,7 @@ export function DataGrid({
)} )}
<Row alignItems="center" gap style={{ marginLeft: 'auto' }}> <Row alignItems="center" gap style={{ marginLeft: 'auto' }}>
{renderActions?.()} {renderActions?.()}
{viewToggleButton} {!isMobile && viewToggleButton}
</Row> </Row>
</Row> </Row>
<LoadingPanel <LoadingPanel
-7
View File
@@ -62,13 +62,6 @@ export function EventsChart({ websiteId, focusLabel, limit }: EventsChartProps)
], ],
}; };
} else { } else {
// Each label has a preferred palette slot derived from a hash of the
// label, so the same event tends to get the same color across reloads
// and date-range changes. We walk labels in hash order and, when two
// labels prefer the same slot, the later one steps to the next free
// slot, so the visible set of <=12 events all get distinct colors.
// The right shift on hex6 sidesteps the FNV-1a low-bit bias mod 12
// (the FNV prime is close to 2^24).
const colorByKey: Record<string, string> = {}; const colorByKey: Record<string, string> = {};
const used = new Set<string>(); const used = new Set<string>();
const hashOf = Object.fromEntries( const hashOf = Object.fromEntries(
-5
View File
@@ -16,11 +16,6 @@ export const IP_ADDRESS_HEADERS = [
'x-forwarded', 'x-forwarded',
]; ];
/**
* Normalize IP strings to a canonical form:
* - strips IPv4-mapped IPv6 (e.g. ::ffff:192.0.2.1 -> 192.0.2.1)
* - keeps valid IPv4/IPv6 as-is (canonically formatted by ipaddr.js)
*/
function normalizeIp(ip?: string | null) { function normalizeIp(ip?: string | null) {
if (!ip) return ip; if (!ip) return ip;
-3
View File
@@ -130,9 +130,6 @@ export async function deleteUser(userId: string) {
const teamIds = teams.map(a => a.id); const teamIds = teams.map(a => a.id);
// Cloud mode keeps owned teams (and their team-owned content), so cleanup
// only covers user-direct rows. Non-cloud hard-deletes owned teams below,
// so we must also clean up team-owned content.
const ownedFilter = cloudMode const ownedFilter = cloudMode
? { userId } ? { userId }
: { OR: [{ userId }, { teamId: { in: teamIds } }] }; : { OR: [{ userId }, { teamId: { in: teamIds } }] };