Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
56 changes: 37 additions & 19 deletions ecosystem-explorer/src/features/collector/collector-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Expand Down Expand Up @@ -239,26 +241,42 @@ function CollectorPageInner({ urlVersion }: { urlVersion?: string }) {
</div>
</div>

<p className="text-muted-foreground/80 line-clamp-3 flex-1 text-sm leading-relaxed">
{comp.description ||
"Browse technical details and configuration options for this component."}
</p>
{comp.description && (
<p className="text-muted-foreground/80 line-clamp-3 flex-1 text-sm leading-relaxed">
{comp.description}
</p>
)}

<div className="border-border/10 flex items-center gap-2 border-t pt-2">
{comp.status?.stability &&
Object.keys(comp.status.stability).length > 0 && (
<GlowBadge
variant={
Object.keys(comp.status.stability)[0] === "stable"
? "success"
: "info"
}
className="px-2 py-0 text-[9px]"
>
{Object.keys(comp.status.stability)[0]}
</GlowBadge>
)}
</div>
{comp.status?.stability && Object.keys(comp.status.stability).length > 0 && (
<div className="border-border/10 flex flex-col gap-1.5 border-t pt-2">
{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]) => (
<div key={level} className="flex flex-wrap items-center gap-1.5">
<GlowBadge
variant={
level === "stable"
? "success"
: level === "beta"
? "info"
: "warning"
}
className="px-2 py-0 text-[9px]"
>
{level}
</GlowBadge>
<span className="text-muted-foreground text-[10px]">
{signals.join(", ")}
</span>
</div>
))}
</div>
)}
Comment thread
aabhinavvvvvvv marked this conversation as resolved.
</div>
</DetailCard>
</Link>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -276,8 +276,9 @@ export function InstrumentationDetailPage() {
</div>

<Tabs value={activeTab} onValueChange={setActiveTab} className="relative z-10">
<div className="overflow-x-auto px-4 pt-4 pb-0 sm:px-6">
<div className="px-4 pt-4 pb-0 sm:px-6">
<SegmentedTabList
fullWidth
value={activeTab}
tabs={[
{
Expand Down