diff --git a/CHANGELOG.md b/CHANGELOG.md index eab6c232..1c32a6d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## v1.4.1 [TBD] _Bug Fixes_ +- Improve benchmark/detection summary chart width calculations. ([#945](https://github.com/turbot/powerpipe/issues/945)) - Fix filtering by false boolean values in tables. ([#946](https://github.com/turbot/powerpipe/issues/946)) ## v1.4.0 [2025-09-22] diff --git a/ui/dashboard/src/components/dashboards/grouping/CheckSummaryChart/index.tsx b/ui/dashboard/src/components/dashboards/grouping/CheckSummaryChart/index.tsx index d51e44ea..c3ac0b53 100644 --- a/ui/dashboard/src/components/dashboards/grouping/CheckSummaryChart/index.tsx +++ b/ui/dashboard/src/components/dashboards/grouping/CheckSummaryChart/index.tsx @@ -1,7 +1,8 @@ import IntegerDisplay from "../../../IntegerDisplay"; +import usePrevious from "@powerpipe/hooks/usePrevious"; import { CheckNodeStatus, CheckSummary } from "../common"; import { classNames } from "@powerpipe/utils/styles"; -import { ReactNode } from "react"; +import { ReactNode, useEffect, useRef, useState } from "react"; type ProgressBarGroupProps = { children: ReactNode; @@ -10,7 +11,7 @@ type ProgressBarGroupProps = { type ProgressBarProps = { className?: string; - percent: number; + width: number; }; type CheckSummaryChartProps = { @@ -34,7 +35,7 @@ type ProgressBarGroupTotalProps = { total: number; }; -const getWidth = (x, y) => { +const getWidth = (x: number, y: number) => { const percent = (x / (x + y)) * 100; return percent >= 0.5 ? Math.round(percent) : 1; }; @@ -89,7 +90,7 @@ const NonAlertProgressBarGroupTotal = ({ summary, }: NonAlertProgressBarGroupTotalProps) => { const nonAlertTotal = summary.ok + summary.info + summary.skip; - let textClassName; + let textClassName: string; if (nonAlertTotal === 0) { textClassName = "text-foreground-lightest"; } else if (summary.skip > summary.info && summary.skip > summary.ok) { @@ -115,29 +116,30 @@ export const ProgressBarGroup = ({ ); -export const ProgressBar = ({ className, percent }: ProgressBarProps) => { - if (!percent) { +export const ProgressBar = ({ className, width }: ProgressBarProps) => { + if (!width) { return null; } return (
); }; -export const getCheckSummaryChartPercent = (value, total) => { - if (!value) { +const getSegmentPixelWidth = ( + value: number, + divisor: number, + containerWidth: number, +) => { + if (!value || !divisor || !containerWidth) { return 0; } - const percentOfTotal = value / total; - const rounded = Math.floor(percentOfTotal * 100); - return Math.max(rounded, 3); + + const percent = value / divisor; + return Math.max(Math.round(percent * containerWidth), 1); }; const CheckSummaryChart = ({ @@ -145,6 +147,31 @@ const CheckSummaryChart = ({ summary, firstChildSummaries, }: CheckSummaryChartProps) => { + const [, setVersion] = useState(0); + const alertsContainerRef = useRef(null); + const nonAlertsContainerRef = useRef(null); + const previousContainers = usePrevious<{ + alerts: HTMLDivElement | null; + nonAlerts: HTMLDivElement | null; + }>({ + alerts: alertsContainerRef.current, + nonAlerts: nonAlertsContainerRef.current, + }); + + useEffect(() => { + if ( + (!previousContainers && + (alertsContainerRef.current || nonAlertsContainerRef.current)) || + (previousContainers && + (previousContainers.alerts !== alertsContainerRef.current || + previousContainers.nonAlerts !== nonAlertsContainerRef.current)) + ) { + // Trigger a re-render when the container refs change + // This is necessary to recalculate widths based on new container sizes + setVersion((v) => v + 1); + } + }, [previousContainers]); + let maxAlerts = 0; let maxNonAlerts = 0; for (const firstChildSummary of firstChildSummaries) { @@ -158,70 +185,124 @@ const CheckSummaryChart = ({ maxNonAlerts = currentMaxNonAlerts; } } - // const [alarm, error, ok, info, skip] = ensureMinPercentages(name, [ - // summary.alarm, - // summary.error, - // summary.ok, - // summary.info, - // summary.skip, - // ]); - let alertsWidth = getWidth(maxAlerts, maxNonAlerts); - let nonAlertsWidth = getWidth(maxNonAlerts, maxAlerts); - if (alertsWidth > nonAlertsWidth) { - alertsWidth -= 2; - } else { - nonAlertsWidth -= 2; - } + const alertsWidth = getWidth(maxAlerts, maxNonAlerts) * 0.9; + const nonAlertsWidth = getWidth(maxNonAlerts, maxAlerts) * 0.9; + + const calculateWidths = () => { + if (!alertsContainerRef.current || !nonAlertsContainerRef.current) { + return { + renderDivider: false, + alarm: 0, + error: 0, + ok: 0, + info: 0, + skip: 0, + }; + } + + const alertsContainerWidth = alertsContainerRef.current?.clientWidth ?? 0; + const nonAlertsContainerWidth = + nonAlertsContainerRef.current?.clientWidth ?? 0; + + const rawAlarm = getSegmentPixelWidth( + summary.alarm, + maxAlerts, + alertsContainerWidth, + ); + const rawError = getSegmentPixelWidth( + summary.error, + maxAlerts, + alertsContainerWidth, + ); + const rawOk = getSegmentPixelWidth( + summary.ok, + maxNonAlerts, + nonAlertsContainerWidth, + ); + const rawInfo = getSegmentPixelWidth( + summary.info, + maxNonAlerts, + nonAlertsContainerWidth, + ); + const rawSkip = getSegmentPixelWidth( + summary.skip, + maxNonAlerts, + nonAlertsContainerWidth, + ); + + return { + alertsContainerWidth, + nonAlertsContainerWidth, + renderDivider: true, + alarm: rawAlarm, + error: rawError, + ok: rawOk, + info: rawInfo, + skip: rawSkip, + }; + }; + + const widths = calculateWidths(); return (
-
+
+ {widths.renderDivider && ( +
+ )}
-
+ ref={nonAlertsContainerRef} + className="my-auto px-0" + style={{ width: `${nonAlertsWidth}%` }} + >