Skip to content
4 changes: 2 additions & 2 deletions src/components/models/ModelCard.astro
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ const authorLogo = authorData[model.author]?.logo;
}
<div class="flex min-w-0 flex-1 items-center gap-2">
<h3
class="text-foreground group-hover/card:text-primary line-clamp-2 text-sm leading-snug font-semibold break-words"
class="text-foreground group-hover/card:text-primary min-w-0 text-sm leading-snug font-semibold break-words"
>
{model.shortName}
</h3>
Expand All @@ -132,7 +132,7 @@ const authorLogo = authorData[model.author]?.logo;
</div>

<p
class="text-muted-foreground mb-4 line-clamp-2 min-h-10 flex-1 text-sm leading-relaxed"
class="text-muted-foreground mb-4 min-h-10 flex-1 text-sm leading-relaxed break-words"
>
{model.description}
</p>
Expand Down
57 changes: 57 additions & 0 deletions src/components/models/ModelCard.astro.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { experimental_AstroContainer as AstroContainer } from "astro/container";
import { parse } from "node-html-parser";
import { describe, expect, test } from "vitest";

import type { ModelCardData } from "~/util/models";
import ModelCard from "./ModelCard.astro";

const model: ModelCardData = {
id: "model-card-fixture",
name: "fixture-author/verylongmodelnamethatmustwrapwithouttruncation",
slug: "fixture-author/verylongmodelnamethatmustwrapwithouttruncation",
displayName: "Very Long Model",
shortName: "verylongmodelnamethatmustwrapwithouttruncation",
author: "fixture-author",
authorName: "Fixture Author",
hosting: "proxied",
dataSource: "catalog",
source: 2,
task: "Text Generation",
description:
"A deliberately long model description that exceeds two lines and must remain fully visible in the model card instead of being clipped by a presentation-only line clamp.",
capabilities: [],
beta: true,
properties: {},
propertiesList: [],
};

const truncationClasses = (className: string | undefined): string[] =>
(className ?? "")
.split(/\s+/)
.filter((token) =>
/^(?:line-clamp-(?:[1-9]\d*|\[[^\]]+\])$|truncate$|overflow-(?:[xy]-)?(?:hidden|clip|ellipsis)$|text-ellipsis$|whitespace-nowrap$)/.test(
token,
),
);

describe("ModelCard", () => {
test("does not emit truncation classes for titles and descriptions", async () => {
const container = await AstroContainer.create();
const html = await container.renderToString(ModelCard, {
props: { model, index: 0, cols: 3 },
});
const root = parse(html);
const title = root.querySelector("h3");
const titleRow = title?.parentNode;
const description = root.querySelector("a > p");

expect(title?.textContent).toBe(model.shortName);
expect(truncationClasses(titleRow?.getAttribute("class"))).toEqual([]);
expect(truncationClasses(title?.getAttribute("class"))).toEqual([]);
expect(title?.getAttribute("class")).toContain("min-w-0");
expect(title?.getAttribute("class")).toContain("break-words");
expect(description?.textContent).toBe(model.description);
expect(truncationClasses(description?.getAttribute("class"))).toEqual([]);
expect(description?.getAttribute("class")).toContain("break-words");
});
});