diff --git a/frontend/src/components/Dashboard/ui/fluksio/Data.tsx b/frontend/src/components/Dashboard/ui/fluksio/Data.tsx index 15f0ba7..5076548 100644 --- a/frontend/src/components/Dashboard/ui/fluksio/Data.tsx +++ b/frontend/src/components/Dashboard/ui/fluksio/Data.tsx @@ -64,6 +64,9 @@ export function Gauge({ value, min, max, precision, unit, label }: GaugeProps) { ? 0 : Math.min(1, Math.max(0, (value - min) / (max - min || 1))), ) + // At the minimum the reading is a zero-length dash, and its round caps would + // still paint a dot on the dial. Nothing reads there, so nothing is drawn. + const drawn = useTransform(fraction, (at) => (at > 0 ? 1 : 0)) const { label: reading, numeric } = useAnimatedNumber(value, precision) return (
@@ -86,7 +89,7 @@ export function Gauge({ value, min, max, precision, unit, label }: GaugeProps) { fraction itself. */} (at > 0 ? 1 : 0)) const { label: reading, numeric } = useAnimatedNumber(value, precision) return (
@@ -91,6 +94,7 @@ export function Gauge({ value, min, max, precision, unit, label }: GaugeProps) { // fills and the active controls do. style={{ pathLength: fraction, + opacity: drawn, filter: "drop-shadow(0 0 4px var(--primary))", }} fill="none" diff --git a/frontend/src/components/Dashboard/ui/material/Data.tsx b/frontend/src/components/Dashboard/ui/material/Data.tsx index ce1911d..86b0cd2 100644 --- a/frontend/src/components/Dashboard/ui/material/Data.tsx +++ b/frontend/src/components/Dashboard/ui/material/Data.tsx @@ -62,6 +62,9 @@ export function Gauge({ value, min, max, precision, unit, label }: GaugeProps) { ? 0 : Math.min(1, Math.max(0, (value - min) / (max - min || 1))), ) + // At the minimum the reading is a zero-length dash, and its round caps would + // still paint a dot on the dial. Nothing reads there, so nothing is drawn. + const drawn = useTransform(fraction, (at) => (at > 0 ? 1 : 0)) const { label: reading, numeric } = useAnimatedNumber(value, precision) return (
@@ -84,7 +87,7 @@ export function Gauge({ value, min, max, precision, unit, label }: GaugeProps) { fraction itself. */} `${flowName}.${name}` /** Every tile the dashboard carries, including the deliberately unbound one. */ -const TILES = 9 +const TILES = 11 test.use({ storageState: "playwright/.auth/user.json" }) @@ -58,6 +58,7 @@ test.beforeAll(async ({ browser }) => { { name: "mode", dtype: "str" }, { name: "lamp", dtype: "bool" }, { name: "setpoint", dtype: "float" }, + { name: "idle", dtype: "float" }, ], }, ], @@ -76,6 +77,7 @@ test.beforeAll(async ({ browser }) => { await publish(page, w("grid"), 20) await publish(page, w("condition"), "sun") await publish(page, w("setpoint"), 21.5) + await publish(page, w("idle"), 0) await publish(page, w("days"), [ { label: "Mon", icon: "sun", value: "21°" }, { label: "Tue", icon: "cloudy", value: "18°" }, @@ -197,6 +199,14 @@ test.beforeAll(async ({ browser }) => { unit: "°C", }, }, + { + // At its minimum, where a gauge draws no reading at all. + id: "idle", + type: "gauge", + title: "Idle", + layout: { lg: { x: 4, y: 6, w: 2, h: 2 } }, + config: { message: w("idle"), dtype: "float", min: 0, max: 100 }, + }, { id: "trend", type: "chart", @@ -716,6 +726,10 @@ for (const scheme of ["light", "dark"] as const) { // Every widget still draws, whichever set is asked to draw it. await expect(page.getByTestId("widget-frame")).toHaveCount(TILES) await expect(loadTile(page).getByTestId("bar-row")).toHaveCount(2) + // The reading is a zero-length dash at the minimum, and round caps + // would still paint it as a dot at either end of the dial. + const dial = page.getByRole("img", { name: /^Idle:/ }) + await expect(dial.locator("path").nth(1)).toHaveCSS("opacity", "0") await page.screenshot({ path: `screenshots/widgets/${scheme}-${look}.png`,