Skip to content
Merged
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
10 changes: 10 additions & 0 deletions ecosystem-explorer/src/components/ui/detail-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@
* limitations under the License.
*/
import React from "react";
import type { CollectorComponentType } from "./type-stripe-colors";
import { TypeStripe } from "./type-stripe";

interface DetailCardProps {
children: React.ReactNode;
className?: string;
withGrid?: boolean;
withHoverEffect?: boolean;
typeStripe?: CollectorComponentType;
}

export function DetailCard({
children,
className = "",
withGrid = false,
withHoverEffect = false,
typeStripe,
}: DetailCardProps) {
const patternId = React.useId().replace(/:/g, "-");

Expand All @@ -38,6 +42,12 @@ export function DetailCard({
: ""
} ${className}`}
>
{typeStripe && (
<TypeStripe
type={typeStripe}
className="pointer-events-none absolute inset-y-0 left-0 w-1"
/>
)}
{withGrid && (
<div className="absolute inset-0 opacity-10">
<svg className="h-full w-full" xmlns="http://www.w3.org/2000/svg">
Expand Down
34 changes: 34 additions & 0 deletions ecosystem-explorer/src/components/ui/type-stripe-colors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/*
* TypeStripe color map — shared between the standalone `<TypeStripe>`
* primitive and the `typeStripe` slot on `<DetailCard>`. Lives in its own
* file so React Fast Refresh stays happy (`react-refresh/only-export-components`
* disallows constants alongside component exports).
*/

import type { CollectorComponent } from "@/types/collector";

export type CollectorComponentType = CollectorComponent["type"];

export const TYPE_STRIPE_COLORS: Record<CollectorComponentType, string> = {
receiver: "hsl(200 85% 45%)",
processor: "hsl(270 70% 55%)",
exporter: "hsl(38 95% 52%)",
connector: "hsl(330 75% 50%)",
extension: "hsl(165 70% 40%)",
};
45 changes: 45 additions & 0 deletions ecosystem-explorer/src/components/ui/type-stripe.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { render } from "@testing-library/react";
import { describe, it, expect } from "vitest";
import { type CollectorComponentType, TYPE_STRIPE_COLORS } from "./type-stripe-colors";
import { TypeStripe } from "./type-stripe";

const types: CollectorComponentType[] = [
"receiver",
"processor",
"exporter",
"connector",
"extension",
];

describe("TypeStripe", () => {
it.each(types)("renders an aria-hidden element with type=%s and the matching color", (type) => {
const { container } = render(<TypeStripe type={type} />);
const el = container.querySelector(".type-stripe");
expect(el).not.toBeNull();
expect(el).toHaveAttribute("data-type", type);
expect(el).toHaveAttribute("aria-hidden");
expect(el).toHaveStyle({ backgroundColor: TYPE_STRIPE_COLORS[type] });
});
Comment thread
vitorvasc marked this conversation as resolved.

it("exports a stable color for each of the five canonical types", () => {
expect(Object.keys(TYPE_STRIPE_COLORS).sort()).toEqual(types.slice().sort());
for (const c of Object.values(TYPE_STRIPE_COLORS)) {
expect(c).toMatch(/^hsl\(/);
}
});
});
49 changes: 49 additions & 0 deletions ecosystem-explorer/src/components/ui/type-stripe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
/*
* Copyright The OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/*
* TypeStripe — 4px left-edge color stripe used to flag the OTel-collector
* component type at the leading edge of cards, list rows, and detail headers.
*
* Five canonical types: receiver / processor / exporter / connector /
* extension. Color mapping lives in `./type-stripe-colors.ts` (split out so
* React Fast Refresh stays happy).
*
* Two consumers:
* 1. As a slot inside `<DetailCard typeStripe="...">` — DetailCard mounts
* `<TypeStripe>` with its own positioning className, so the stripe
* rendering stays in one place.
* 2. As a standalone `<TypeStripe type="..." />` for list rows that don't
* use DetailCard (e.g. compact list view in Phase 4).
*/

import { type CollectorComponentType, TYPE_STRIPE_COLORS } from "./type-stripe-colors";

export interface TypeStripeProps {
type: CollectorComponentType;
className?: string;
}

export function TypeStripe({ type, className }: TypeStripeProps) {
return (
<span
aria-hidden
className={className ? `type-stripe ${className}` : "type-stripe"}
data-type={type}
style={{ backgroundColor: TYPE_STRIPE_COLORS[type] }}
/>
);
}
Loading