fix(widgets): restore compact v1 surfaces (#6710)

This commit is contained in:
Thomas authored and GitHub committed 2026-08-27 11:08:17 +02:00
1 parent 1b2f1bd720
commit 643ecf3df7
105 files changed
+1603 -1285

No files matched your search

@@ -10,12 +10,20 @@ import { formatStatsSpeed } from "./helpers";
import { SectionLabel } from "./section-label";
import { SpeedStatCard } from "./speed-stat-card";
export function AveragesSection({ stats, width = 500 }: { stats: SpeedtestTrackerStats; width?: number }) {
export function AveragesSection({
stats,
width = 500,
compactSurface = false,
}: {
stats: SpeedtestTrackerStats;
width?: number;
compactSurface?: boolean;
}) {
const t = useI18n("widget.speedtestTracker");
return (
<Stack gap={6} h="100%">
<SectionLabel>
<SectionLabel uppercase={compactSurface}>
{t("averages")} · {t("tests", { count: stats.total_results.toString() })}
</SectionLabel>
<SimpleGrid cols={width < 220 ? 1 : 3} spacing="xs" style={{ flex: 1, gridTemplateRows: "1fr" }}>
@@ -25,6 +33,7 @@ export function AveragesSection({ stats, width = 500 }: { stats: SpeedtestTracke
value={formatStatsSpeed(stats.download)}
label={t("avgDownload")}
compact
legacySurface={compactSurface}
/>
<SpeedStatCard
icon={IconArrowUp}
@@ -32,6 +41,7 @@ export function AveragesSection({ stats, width = 500 }: { stats: SpeedtestTracke
value={formatStatsSpeed(stats.upload)}
label={t("avgUpload")}
compact
legacySurface={compactSurface}
/>
<SpeedStatCard
icon={IconWaveSine}
@@ -39,6 +49,7 @@ export function AveragesSection({ stats, width = 500 }: { stats: SpeedtestTracke
value={`${stats.ping.avg.toFixed(1)} ms`}
label={t("avgPing")}
compact
legacySurface={compactSurface}
/>
</SimpleGrid>
</Stack>
@@ -11,7 +11,7 @@ import { IntegrationErrorIndicator } from "../common/integration-error-indicator
import { getUsableWidgetQueryData } from "../common/query-state";
import { WidgetEmptyState } from "../common/empty-state";
import { AveragesSection } from "./averages";
import { combineSpeedtestDashboards, getAvailableSpeedtestDashboards, getCompactSections } from "./helpers";
import { combineSpeedtestDashboards, getAvailableSpeedtestDashboards } from "./helpers";
import { LatestResultSection } from "./latest-result";
import { RecentResultsSection } from "./recent-results";
@@ -51,16 +51,19 @@ export default function SpeedtestTrackerWidget({
const advancedSectionWidth = width >= 720 ? width / 2 : width;
const latest = showLatestResult && combined.latestResult && (
<LatestResultSection result={combined.latestResult} width={isAdvanced ? advancedSectionWidth : width} />
<LatestResultSection
result={combined.latestResult}
width={isAdvanced ? advancedSectionWidth : width}
compactSurface={!isAdvanced}
/>
);
const averages = showStats && combined.stats && (
<AveragesSection stats={combined.stats} width={isAdvanced ? advancedSectionWidth : width} />
<AveragesSection
stats={combined.stats}
width={isAdvanced ? advancedSectionWidth : width}
compactSurface={!isAdvanced}
/>
);
const compactSections = getCompactSections(height, {
latest: Boolean(latest),
chart: hasChart,
averages: Boolean(averages),
});
if (displayMode === "advanced") {
const sourceColumns = successfulDashboards.length > 1 && width >= 900 ? 2 : 1;
@@ -136,15 +139,15 @@ export default function SpeedtestTrackerWidget({
{successfulDashboards.map(({ integrationName }) => integrationName).join(" · ")}
</Text>
)}
{(compactSections.latest || compactSections.averages) && (
{(latest || averages) && (
<Stack gap="sm" style={{ flex: 1, minHeight: 0 }}>
{compactSections.latest && <div style={{ flex: 1, minHeight: 0 }}>{latest}</div>}
{compactSections.averages && <div style={{ flex: 1, minHeight: 0 }}>{averages}</div>}
{latest && <div style={{ flex: 1, minHeight: 0 }}>{latest}</div>}
{averages && <div style={{ flex: 1, minHeight: 0 }}>{averages}</div>}
</Stack>
)}
{compactSections.chart && (
{hasChart && (
<div style={{ flex: 2, minHeight: 0 }}>
<RecentResultsSection results={recentFiltered} showPingGraph={showPingGraph} />
<RecentResultsSection results={recentFiltered} showPingGraph={showPingGraph} uppercaseLabels />
</div>
)}
{noSectionsEnabled && (
@@ -10,7 +10,15 @@ import { formatResultSpeed } from "./helpers";
import { SectionLabel } from "./section-label";
import { SpeedStatCard } from "./speed-stat-card";
export function LatestResultSection({ result, width = 500 }: { result: SpeedtestTrackerResult; width?: number }) {
export function LatestResultSection({
result,
width = 500,
compactSurface = false,
}: {
result: SpeedtestTrackerResult;
width?: number;
compactSurface?: boolean;
}) {
const t = useI18n("widget.speedtestTracker");
const locale = useCurrentIntlLocale();
const timestamp = Intl.DateTimeFormat(locale, {
@@ -25,7 +33,7 @@ export function LatestResultSection({ result, width = 500 }: { result: Speedtest
return (
<Stack gap={6} h="100%">
<Group justify="space-between" align="center" wrap="nowrap">
<SectionLabel>{t("latestResult")}</SectionLabel>
<SectionLabel uppercase={compactSurface}>{t("latestResult")}</SectionLabel>
<Text size="xs" c="dimmed" truncate title={timestamp} style={{ minWidth: 0 }}>
{timestamp}
</Text>
@@ -36,18 +44,21 @@ export function LatestResultSection({ result, width = 500 }: { result: Speedtest
color="blue"
value={formatResultSpeed(result, "download")}
label={t("download")}
legacySurface={compactSurface}
/>
<SpeedStatCard
icon={IconArrowUp}
color="teal"
value={formatResultSpeed(result, "upload")}
label={t("upload")}
legacySurface={compactSurface}
/>
<SpeedStatCard
icon={IconWaveSine}
color="orange"
value={result.ping !== null ? `${result.ping.toFixed(1)} ms` : "—"}
label={t("ping")}
legacySurface={compactSurface}
/>
{result.healthy !== null && (
<SpeedStatCard
@@ -55,6 +66,7 @@ export function LatestResultSection({ result, width = 500 }: { result: Speedtest
color={result.healthy ? "green" : "red"}
value={result.healthy ? t("healthy") : t("unhealthy")}
label={t("status")}
legacySurface={compactSurface}
/>
)}
</SimpleGrid>
@@ -114,30 +114,40 @@ function formatTooltipDate(label: number | string | undefined, locale: string):
export function RecentResultsSection({
results,
showPingGraph,
uppercaseLabels = false,
}: {
results: SpeedtestTrackerResult[];
showPingGraph: boolean;
uppercaseLabels?: boolean;
}) {
const t = useI18n("widget.speedtestTracker");
return (
<Stack gap={4} h="100%" style={{ minHeight: 0 }}>
<SectionLabel>{t("recentResults")}</SectionLabel>
<SectionLabel uppercase={uppercaseLabels}>{t("recentResults")}</SectionLabel>
<div style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column", gap: 4 }}>
<SpeedSubChart results={results} showLabel={showPingGraph} />
{showPingGraph && <PingSubChart results={results} />}
<SpeedSubChart results={results} showLabel={showPingGraph} uppercaseLabels={uppercaseLabels} />
{showPingGraph && <PingSubChart results={results} uppercaseLabels={uppercaseLabels} />}
</div>
</Stack>
);
}
function SpeedSubChart({ results, showLabel }: { results: SpeedtestTrackerResult[]; showLabel: boolean }) {
function SpeedSubChart({
results,
showLabel,
uppercaseLabels,
}: {
results: SpeedtestTrackerResult[];
showLabel: boolean;
uppercaseLabels: boolean;
}) {
const t = useI18n("widget.speedtestTracker");
const { ref, height } = useElementSize<HTMLDivElement>();
return (
<div style={{ flex: 2, minHeight: 0, display: "flex", flexDirection: "column", gap: 2 }}>
{showLabel && <SectionLabel>{t("recentResultsSpeed")}</SectionLabel>}
{showLabel && <SectionLabel uppercase={uppercaseLabels}>{t("recentResultsSpeed")}</SectionLabel>}
<div ref={ref} style={{ flex: 1, minHeight: 0 }}>
{height > 0 && <SpeedHistoryChart results={results} height={height} />}
</div>
@@ -145,13 +155,13 @@ function SpeedSubChart({ results, showLabel }: { results: SpeedtestTrackerResult
);
}
function PingSubChart({ results }: { results: SpeedtestTrackerResult[] }) {
function PingSubChart({ results, uppercaseLabels }: { results: SpeedtestTrackerResult[]; uppercaseLabels: boolean }) {
const t = useI18n("widget.speedtestTracker");
const { ref, height } = useElementSize<HTMLDivElement>();
return (
<div style={{ flex: 1, minHeight: 0, display: "flex", flexDirection: "column", gap: 2 }}>
<SectionLabel>{t("recentResultsPing")}</SectionLabel>
<SectionLabel uppercase={uppercaseLabels}>{t("recentResultsPing")}</SectionLabel>
<div ref={ref} style={{ flex: 1, minHeight: 0 }}>
{height > 0 && <PingHistoryChart results={results} height={height} />}
</div>
@@ -1,9 +1,15 @@
import type { ReactNode } from "react";
import { Text } from "@mantine/core";
export function SectionLabel({ children }: { children: ReactNode }) {
export function SectionLabel({ children, uppercase = false }: { children: ReactNode; uppercase?: boolean }) {
return (
<Text size="xs" fw={600} c="dimmed">
<Text
size="xs"
fw={600}
c="dimmed"
tt={uppercase ? "uppercase" : undefined}
style={{ letterSpacing: uppercase ? "0.05em" : undefined }}
>
{children}
</Text>
);
@@ -13,9 +13,17 @@ export interface SpeedStatCardProps {
value: string;
label: string;
compact?: boolean;
legacySurface?: boolean;
}
export function SpeedStatCard({ icon: Icon, color, value, label, compact = false }: SpeedStatCardProps) {
export function SpeedStatCard({
icon: Icon,
color,
value,
label,
compact = false,
legacySurface = false,
}: SpeedStatCardProps) {
const { ref, height, width } = useElementSize<HTMLDivElement>();
const board = useRequiredBoard();
const isWide = width > height + 20;
@@ -23,7 +31,8 @@ export function SpeedStatCard({ icon: Icon, color, value, label, compact = false
const surfaceColor = `var(--mantine-color-${color}-filled)`;
const accentColor = `var(--mantine-color-${color}-5)`;
const surfaceAlpha = compact ? 0.1 : 0.14;
const surfaceBackground = `rgb(from ${surfaceColor} r g b / calc(var(--opacity, 1) * ${surfaceAlpha}))`;
const modernBackground = `rgb(from ${surfaceColor} r g b / calc(var(--opacity, 1) * ${surfaceAlpha}))`;
const surfaceBackground = legacySurface ? `var(--mantine-color-${color}-light)` : modernBackground;
const surfaceBorder = "rgb(from var(--mantine-color-default-border) r g b / calc(var(--opacity, 1) * 0.45))";
return (
@@ -32,7 +41,7 @@ export function SpeedStatCard({ icon: Icon, color, value, label, compact = false
ref={ref}
p={compact ? "xs" : "sm"}
radius={board.itemRadius}
withBorder
withBorder={!legacySurface}
bg={surfaceBackground}
h="100%"
style={{ flex: 1, minWidth: 0, borderColor: surfaceBorder }}