diff --git a/changelog/161.fix.md b/changelog/161.fix.md new file mode 100644 index 00000000..307b6d17 --- /dev/null +++ b/changelog/161.fix.md @@ -0,0 +1 @@ +Loading more passing groups on the failures page no longer hides the groups already loaded when it fails, and a failure to load the model list is now reported with a retry. diff --git a/changelog/161.improvement.md b/changelog/161.improvement.md new file mode 100644 index 00000000..4ffec298 --- /dev/null +++ b/changelog/161.improvement.md @@ -0,0 +1 @@ +The diagnostic dots on the failures page now turn grey and gently swell while the counts for new filters load. diff --git a/frontend/src/components/failures/health.test.tsx b/frontend/src/components/failures/health.test.tsx new file mode 100644 index 00000000..b78f0d1e --- /dev/null +++ b/frontend/src/components/failures/health.test.tsx @@ -0,0 +1,19 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it } from "vitest"; +import { HEALTH, HealthDot } from "./health"; + +describe("HealthDot", () => { + it("shows the health colour", () => { + render(); + const dot = screen.getByRole("img", { name: "All failing" }); + expect(dot).toHaveClass(HEALTH.failed.colour); + expect(dot).not.toHaveClass("motion-safe:animate-swell"); + }); + + it("goes grey and swells while pending, hiding the stale health", () => { + render(); + const dot = screen.getByRole("img", { name: "Updating" }); + expect(dot).toHaveClass(HEALTH.not_run.colour, "motion-safe:animate-swell"); + expect(dot).not.toHaveClass(HEALTH.failed.colour); + }); +}); diff --git a/frontend/src/components/failures/health.tsx b/frontend/src/components/failures/health.tsx index 1d35f8a3..e1ccf14d 100644 --- a/frontend/src/components/failures/health.tsx +++ b/frontend/src/components/failures/health.tsx @@ -1,3 +1,4 @@ +import type React from "react"; import type { DiagnosticHealth } from "@/lib/failures"; import { cn } from "@/lib/utils"; @@ -28,20 +29,28 @@ export const HEALTH = { { label: string; description: string; colour: string } >; +/** A diagnostic's health, or a grey swelling dot while `pending` new counts */ export function HealthDot({ health, + pending = false, className, + style, }: { health: DiagnosticHealth; + pending?: boolean; className?: string; + style?: React.CSSProperties; }) { return ( diff --git a/frontend/src/components/failures/passingGroups.tsx b/frontend/src/components/failures/passingGroups.tsx index 5ba6ada4..866c8586 100644 --- a/frontend/src/components/failures/passingGroups.tsx +++ b/frontend/src/components/failures/passingGroups.tsx @@ -53,24 +53,37 @@ function PassingGroupList({ mipEra: MipEra; model?: string; }) { - const { data, error, fetchNextPage, hasNextPage, isFetchingNextPage } = - useInfiniteQuery({ - ...executionsListRecentExecutionGroupsInfiniteOptions({ - query: { - successful: true, - provider_slug: diagnostic.provider.slug, - diagnostic_slug: diagnostic.slug, - mip_era: mipEra, - source_id: model, - limit: PAGE_SIZE, - }, - }), - initialPageParam: 0, - getNextPageParam: (_lastPage, allPages) => nextOffset(allPages), - }); + const { + data, + error, + refetch, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + } = useInfiniteQuery({ + ...executionsListRecentExecutionGroupsInfiniteOptions({ + query: { + successful: true, + provider_slug: diagnostic.provider.slug, + diagnostic_slug: diagnostic.slug, + mip_era: mipEra, + source_id: model, + limit: PAGE_SIZE, + }, + }), + initialPageParam: 0, + getNextPageParam: (_lastPage, allPages) => nextOffset(allPages), + }); - if (error) { - return

{apiErrorMessage(error)}

; + if (!data && error) { + return ( +
+

{apiErrorMessage(error)}

+ +
+ ); } if (!data) { return ( @@ -90,6 +103,9 @@ function PassingGroupList({ ))} + {error && ( +

{apiErrorMessage(error)}

+ )} {hasNextPage && (