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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
"build": "npm run generate:data && npm run validate:data && vite build && jiti scripts/prepare-pages-fallback.ts",
"preview": "vite preview",
"verify": "npx tsc --noEmit && node scripts/check_shortcode_syntax.js content && npm run build && npm run verify:artifact",
"test": "npm run test:visual-review && npm run test:ability-damage-evidence && tsx scripts/dev-review.test.ts && tsx scripts/asset-dump-resolver.test.ts && tsx scripts/blood-hunts.test.ts && tsx scripts/npc-portraits.test.ts && tsx scripts/buildable-portraits.test.ts && tsx --test src/pages/HomePage.test.tsx && tsx --test src/components/layout/SiteShell.test.tsx && tsx --test src/components/db/DbDetailView.test.tsx",
"test": "npm run test:visual-review && npm run test:ability-damage-evidence && tsx scripts/dev-review.test.ts && tsx scripts/asset-dump-resolver.test.ts && tsx scripts/blood-hunts.test.ts && tsx scripts/npc-portraits.test.ts && tsx scripts/buildable-portraits.test.ts && tsx --test src/pages/HomePage.test.tsx && tsx --test src/components/layout/SiteShell.test.tsx && tsx --test src/components/db/DbDetailView.test.tsx && tsx --test src/components/reference/ReferenceDetailView.test.tsx",
"visual:baseline": "npm run build && npm run visual:baseline:capture",
"visual:baseline:capture": "jiti scripts/visual-review.ts baseline",
"visual:compare": "npm run build && npm run visual:compare:capture",
Expand Down
2 changes: 2 additions & 0 deletions scripts/build-reference-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -779,6 +779,7 @@ async function main() {
}

function registerPrefabReference(doc: SourceDocument, guid: number | null, categories: string[], components: ParsedPrefabComponent[]): void {
const componentDocCount = components.filter((component) => component.path).length;
const componentRelations = components.map((component) => ({
title: component.name,
path: component.path,
Expand Down Expand Up @@ -837,6 +838,7 @@ async function main() {
toRow("GUID", guid, { monospace: true }),
toRow("Categories", categories.length),
toRow("Components", components.length),
toRow("Component Docs", `${componentDocCount} / ${components.length}`),
toRow("Nested Entry Blocks", components.filter((component) => component.entries.length > 0).length)
].filter((row): row is ReferenceFieldRow => Boolean(row)),
detailSections,
Expand Down
78 changes: 78 additions & 0 deletions src/components/reference/ReferenceDetailView.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import assert from "node:assert/strict";
import test from "node:test";
import React from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { MemoryRouter } from "react-router-dom";
import { ReferenceDetail } from "../../types/reference";
import { ReferenceDetailView } from "./ReferenceDetailView";

function renderWithoutLayoutWarning(render: () => string) {
const originalError = console.error;
console.error = (...args: unknown[]) => {
const first = String(args[0] ?? "");
if (first.includes("useLayoutEffect does nothing on the server")) {
return;
}

originalError(...args);
};

try {
return render();
} finally {
console.error = originalError;
}
}

function renderReferenceDetail(detail: ReferenceDetail) {
return renderWithoutLayoutWarning(() =>
renderToStaticMarkup(
<MemoryRouter>
<ReferenceDetailView detail={detail} />
</MemoryRouter>
)
);
}

test("prefab component relations explain snapshot source and doc coverage", () => {
const detail: ReferenceDetail = {
section: "prefabs",
kind: "prefab",
slug: "char-test-vblood",
title: "CHAR_Test_VBlood",
path: "/prefabs/char-test-vblood",
sourcePath: "content/prefabs/CHAR_Test_VBlood.md",
excerpt: "Test prefab.",
tags: ["CHAR_Test_VBlood"],
stats: [
{ label: "Components", value: "2" },
{ label: "Component Docs", value: "1 / 2" }
],
relationGroups: [
{
title: "Components",
totalCount: 2,
items: [
{ title: "ProjectM.AbilityBar_Server", path: "/components/abilitybar-server", description: "10 fields" },
{ title: "ProjectM.IdleInteractor", description: "3 fields" }
]
},
{
title: "Collections",
totalCount: 1,
items: [{ title: "CHAR", path: "/prefabs/char", description: "Collection" }]
}
]
};

const html = renderReferenceDetail(detail);

assert.match(html, /2 components/);
assert.doesNotMatch(html, /2 linked/);
assert.match(html, /extracted prefab component snapshot/);
assert.match(html, /Linked rows open generated component docs/);
assert.match(html, /No component doc/);
assert.match(html, /href="\/components\/abilitybar-server"/);
assert.match(html, /Component Docs/);
assert.match(html, /1 \/ 2/);
});
45 changes: 42 additions & 3 deletions src/components/reference/ReferenceDetailView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { DetailJumpItem, DetailJumpStrip } from "../common/DetailJumpStrip";
import { CollapsibleTextBlock } from "../common/CollapsibleTextBlock";
import { CopyValueButton } from "../common/CopyValueButton";
import { headingId } from "../../lib/text";
import { ReferenceDetail, ReferenceRelationGroup } from "../../types/reference";
import { ReferenceDetail, ReferenceRelation, ReferenceRelationGroup } from "../../types/reference";
import { ReferenceBadge, ReferenceFieldGrid, ReferenceRelationList, ReferenceSurface } from "./ReferenceUi";

function getMonogram(value: string): string {
Expand Down Expand Up @@ -96,6 +96,42 @@ function buildJumpItems(detail: ReferenceDetail, relationGroups: ReferenceRelati
return items;
}

function isPrefabComponentsGroup(detail: ReferenceDetail, group: ReferenceRelationGroup): boolean {
return detail.section === "prefabs" && group.title === "Components";
}

function getRelationMeta(detail: ReferenceDetail, group: ReferenceRelationGroup): string {
const count = group.totalCount ?? group.items.length;
return isPrefabComponentsGroup(detail, group) ? `${count} components` : `${count} linked`;
}

function getRelationItems(detail: ReferenceDetail, group: ReferenceRelationGroup): ReferenceRelation[] {
if (!isPrefabComponentsGroup(detail, group)) {
return group.items;
}

return group.items.map((item) =>
item.path
? item
: {
...item,
badges: item.badges?.includes("No component doc") ? item.badges : [...(item.badges ?? []), "No component doc"]
}
);
}

function renderPrefabComponentNote(detail: ReferenceDetail, group: ReferenceRelationGroup) {
if (!isPrefabComponentsGroup(detail, group)) {
return null;
}

return (
<p className="database-panel-subtle rounded-[1rem] px-4 py-3 text-xs leading-6 text-[var(--database-muted)]">
Component rows come from an extracted prefab component snapshot. Linked rows open generated component docs; unlinked rows are still attached components without a matching component doc page.
</p>
);
}

function renderSummaryRows(detail: ReferenceDetail) {
const stats = detail.stats ?? [];
if (stats.length === 0) {
Expand Down Expand Up @@ -185,9 +221,12 @@ export function ReferenceDetailView({ detail }: { detail: ReferenceDetail }) {
key={group.title}
title={group.title}
anchorId={`relation-${headingId(group.title)}`}
meta={`${group.totalCount ?? group.items.length} linked`}
meta={getRelationMeta(detail, group)}
>
<ReferenceRelationList items={group.items} emptyLabel={group.emptyLabel} totalCount={group.totalCount} />
<div className="space-y-4">
{renderPrefabComponentNote(detail, group)}
<ReferenceRelationList items={getRelationItems(detail, group)} emptyLabel={group.emptyLabel} totalCount={group.totalCount} />
</div>
</ReferenceSurface>
))}

Expand Down
Loading