diff --git a/ecosystem-explorer/src/features/collector/collector-page.test.tsx b/ecosystem-explorer/src/features/collector/collector-page.test.tsx index 99d537eb..0ec7d4c9 100644 --- a/ecosystem-explorer/src/features/collector/collector-page.test.tsx +++ b/ecosystem-explorer/src/features/collector/collector-page.test.tsx @@ -203,6 +203,78 @@ describe("CollectorPage", () => { expect(screen.getByText(/Showing/)).toHaveTextContent("Showing 2 components"); }); + it("shows all stability levels with signals and no fallback description for components without descriptions", () => { + const componentWithoutDescription: CollectorComponent = { + id: "exporter-elasticsearch", + name: "elasticsearchexporter", + display_name: "Elasticsearch Exporter", + description: null, + ecosystem: "collector", + type: "exporter", + distribution: "contrib", + status: { + class: "exporter", + stability: { beta: ["logs", "traces"], development: ["metrics", "profiles"] }, + distributions: ["contrib"], + }, + }; + + vi.mocked(useCollectorVersions).mockReturnValue({ + data: mockVersionsData, + loading: false, + error: null, + }); + vi.mocked(useCollectorComponents).mockReturnValue({ + data: [componentWithoutDescription], + loading: false, + error: null, + }); + + renderAtRoute("/collector/components"); + + expect( + screen.queryByText("Browse technical details and configuration options for this component.") + ).not.toBeInTheDocument(); + expect(screen.getByText("beta")).toBeInTheDocument(); + expect(screen.getByText("logs, traces")).toBeInTheDocument(); + expect(screen.getByText("development")).toBeInTheDocument(); + expect(screen.getByText("metrics, profiles")).toBeInTheDocument(); + }); + + it("renders stability levels in precedence order regardless of JSON key order", () => { + const componentWithUnsortedStability: CollectorComponent = { + id: "receiver-otlp-unsorted", + name: "otlpreceiver-unsorted", + display_name: "OTLP Receiver Unsorted", + description: null, + ecosystem: "collector", + type: "receiver", + distribution: "core", + status: { + class: "receiver", + stability: { alpha: ["profiles"], stable: ["logs", "metrics", "traces"] }, + distributions: ["core"], + }, + }; + + vi.mocked(useCollectorVersions).mockReturnValue({ + data: mockVersionsData, + loading: false, + error: null, + }); + vi.mocked(useCollectorComponents).mockReturnValue({ + data: [componentWithUnsortedStability], + loading: false, + error: null, + }); + + renderAtRoute("/collector/components"); + + const badges = screen.getAllByText(/^(stable|alpha)$/); + expect(badges[0]).toHaveTextContent("stable"); + expect(badges[1]).toHaveTextContent("alpha"); + }); + it("renders version selector with available versions", () => { vi.mocked(useCollectorVersions).mockReturnValue({ data: mockVersionsData, diff --git a/ecosystem-explorer/src/features/collector/collector-page.tsx b/ecosystem-explorer/src/features/collector/collector-page.tsx index 247a42d8..5bf39242 100644 --- a/ecosystem-explorer/src/features/collector/collector-page.tsx +++ b/ecosystem-explorer/src/features/collector/collector-page.tsx @@ -34,6 +34,8 @@ import { DetailCard } from "@/components/ui/detail-card"; import { useCollectorVersions, useCollectorComponents } from "@/hooks/use-collector-data"; import { isEnabled } from "@/lib/feature-flags"; +const STABILITY_ORDER = ["stable", "beta", "alpha", "development", "deprecated", "unmaintained"]; + const getIcon = (type: string) => { switch (type) { case "receiver": @@ -239,26 +241,42 @@ function CollectorPageInner({ urlVersion }: { urlVersion?: string }) { -

- {comp.description || - "Browse technical details and configuration options for this component."} -

+ {comp.description && ( +

+ {comp.description} +

+ )} -
- {comp.status?.stability && - Object.keys(comp.status.stability).length > 0 && ( - - {Object.keys(comp.status.stability)[0]} - - )} -
+ {comp.status?.stability && Object.keys(comp.status.stability).length > 0 && ( +
+ {Object.entries(comp.status.stability) + .filter(([, signals]) => signals.length > 0) + .sort(([a], [b]) => { + const ai = STABILITY_ORDER.indexOf(a); + const bi = STABILITY_ORDER.indexOf(b); + return (ai === -1 ? Infinity : ai) - (bi === -1 ? Infinity : bi); + }) + .map(([level, signals]) => ( +
+ + {level} + + + {signals.join(", ")} + +
+ ))} +
+ )} diff --git a/ecosystem-explorer/src/features/java-agent/instrumentation-detail-page.tsx b/ecosystem-explorer/src/features/java-agent/instrumentation-detail-page.tsx index 8292642f..3de98b38 100644 --- a/ecosystem-explorer/src/features/java-agent/instrumentation-detail-page.tsx +++ b/ecosystem-explorer/src/features/java-agent/instrumentation-detail-page.tsx @@ -276,8 +276,9 @@ export function InstrumentationDetailPage() { -
+