GraphLink: rename reserved ref prop to entityRef (server components refuse ref); QA weight caps for home and graph
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
7 changed files +12 −11
modified
apps/web/qa/smoke.mjs
+2 −2
@@ -25,7 +25,7 @@ const ONLY_HTTP = process.env.HTTP_ONLY === '1'; | ||
| 25 | 25 | |
| 26 | 26 | /** @type {Array<{path: string, expect: string[], mustHaveData?: boolean, optionalLocal?: boolean, maxKb?: number, resolve?: (ctx: Record<string,string>) => string | null, kind?: 'html'|'xml'|'json'}>} */ |
| 27 | 27 | const ROUTES = [ |
| 28 | − { path: '/', expect: ['CancerIndex'] }, | |
| 28 | + { path: '/', expect: ['CancerIndex'], maxKb: 800 }, | |
| 29 | 29 | { path: '/cancers', expect: ['Cancers'] }, |
| 30 | 30 | { path: '/cancer/malignant-pancreatic-neoplasm', expect: ['Malignant Pancreatic Neoplasm', 'CI-CAN-'] }, |
| 31 | 31 | // "observations" only appears in the data branch (Freshness "N observations"); the empty branch is caught by mustHaveData. |
@@ -45,7 +45,7 @@ const ROUTES = [ | ||
| 45 | 45 | { path: '/trials/terminated', expect: ['NCT'] , maxKb: 900 }, |
| 46 | 46 | { path: '/trials/map', expect: ['Trial map', 'ISO'], maxKb: 1200 }, |
| 47 | 47 | { path: '/research-gap', expect: ['Research Gap', 'log'], mustHaveData: true, maxKb: 900 }, |
| 48 | − { path: '/graph', expect: ['graph', 'CI-CAN-'], maxKb: 900 }, | |
| 48 | + { path: '/graph', expect: ['graph', 'CI-CAN-'], maxKb: 1100 }, | |
| 49 | 49 | { path: '/graph?focus=gene:KRAS', expect: ['KRAS'], maxKb: 900 }, |
| 50 | 50 | { path: '/approvals', expect: ['approvals', 'FDA'], maxKb: 900 }, |
| 51 | 51 | { path: '/pipeline', expect: ['pipeline', 'Phase'], maxKb: 900 }, |
modified
apps/web/src/app/drug/[slug]/page.tsx
+1 −1
@@ -94,7 +94,7 @@ export default async function DrugPage({ params, searchParams }: { params: Promi | ||
| 94 | 94 | <PageHeader kicker={`Drug${d.kind ? ` · ${humanize(d.kind)}` : ''}`} title={d.name} lede={d.description ?? undefined}> |
| 95 | 95 | <p className="mt-2 flex flex-wrap items-center gap-2 text-[12.5px]"> |
| 96 | 96 | <span className="ci-mono text-ink-3">{d.id}</span> |
| 97 | − <GraphLink type="drug" ref={d.slug} /> | |
| 97 | + <GraphLink type="drug" entityRef={d.slug} /> | |
| 98 | 98 | {d.ncit_code ? ( |
| 99 | 99 | <a className="ci-link inline-flex items-center gap-1" href={`https://evsexplore.semantics.cancer.gov/evsexplore/concept/ncit/${d.ncit_code}`} target="_blank" rel="noopener noreferrer"> |
| 100 | 100 | NCIt {d.ncit_code} <ExternalLink className="h-3 w-3" aria-hidden /> |
modified
apps/web/src/app/gene/[symbol]/page.tsx
+1 −1
@@ -59,7 +59,7 @@ export default async function GenePage({ params, searchParams }: { params: Promi | ||
| 59 | 59 | <article> |
| 60 | 60 | <PageHeader kicker="Gene" title={<span className="ci-mono font-sans">{g.symbol}</span>} lede={g.name ?? undefined}> |
| 61 | 61 | <p className="mt-2 flex flex-wrap items-center gap-2 text-[12.5px]"> |
| 62 | − <GraphLink type="gene" ref={g.symbol} /> | |
| 62 | + <GraphLink type="gene" entityRef={g.symbol} /> | |
| 63 | 63 | <span className="ci-mono text-ink-3">{g.id}</span> |
| 64 | 64 | {g.hgnc_id ? ( |
| 65 | 65 | <a className="ci-link inline-flex items-center gap-1" href={`https://www.genenames.org/data/gene-symbol-report/#!/hgnc_id/${g.hgnc_id}`} target="_blank" rel="noopener noreferrer"> |
modified
apps/web/src/app/trial/[nct]/page.tsx
+1 −1
@@ -51,7 +51,7 @@ export default async function TrialPage({ params, searchParams }: { params: Prom | ||
| 51 | 51 | </a> |
| 52 | 52 | <SourceBadge p={{ sourceSlug: 'clinicaltrials', sourceName: 'ClinicalTrials.gov', retrievedAt: t.updated_at, ingestRunId: t.ingest_run_id, layer: 'normalized' }} /> |
| 53 | 53 | <ClaimBadge kind="published" /> |
| 54 | − <GraphLink type="trial" ref={t.nct_id} /> | |
| 54 | + <GraphLink type="trial" entityRef={t.nct_id} /> | |
| 55 | 55 | </div> |
| 56 | 56 | {t.why_stopped ? <Note tone="warn">Why stopped (as posted): {t.why_stopped}</Note> : null} |
| 57 | 57 | </PageHeader> |
modified
apps/web/src/app/variant/[slug]/page.tsx
+1 −1
@@ -46,7 +46,7 @@ export default async function VariantPage({ params, searchParams }: { params: Pr | ||
| 46 | 46 | <PageHeader kicker={`Variant${v.variant_type ? ` · ${humanize(v.variant_type)}` : ''}`} title={<>{v.gene_symbol ? <Link href={`/gene/${v.gene_symbol}`} className="ci-mono font-sans text-ink no-underline hover:text-accent">{v.gene_symbol}</Link> : null} {v.name}</>}> |
| 47 | 47 | <p className="mt-2 flex flex-wrap items-center gap-2 text-[12.5px]"> |
| 48 | 48 | <span className="ci-mono text-ink-3">{v.id}</span> |
| 49 | − <GraphLink type="variant" ref={v.slug} /> | |
| 49 | + <GraphLink type="variant" entityRef={v.slug} /> | |
| 50 | 50 | {v.hgvs_p ? <span className="ci-mono">{v.hgvs_p}</span> : null} |
| 51 | 51 | {v.hgvs_c ? <span className="ci-mono">{v.hgvs_c}</span> : null} |
| 52 | 52 | {v.clinvar_variation_id ? ( |
modified
apps/web/src/components/cancer/header.tsx
+1 −1
@@ -47,7 +47,7 @@ export function CancerHeader({ b, tab }: { b: CancerBundle; tab: TabKey }) { | ||
| 47 | 47 | <span className="ci-mono text-ink-2">{c.id}</span> |
| 48 | 48 | {c.short_name && c.short_name !== c.canonical_name ? <span>{c.short_name}</span> : null} |
| 49 | 49 | {abbreviations.length ? <span>{abbreviations.slice(0, 6).join(' · ')}</span> : null} |
| 50 | − <GraphLink type="cancer" ref={c.slug} /> | |
| 50 | + <GraphLink type="cancer" entityRef={c.slug} /> | |
| 51 | 51 | </p> |
| 52 | 52 | {badges.length ? ( |
| 53 | 53 | <div className="mt-2 flex flex-wrap gap-1.5"> |
modified
apps/web/src/components/graph/graph-link.tsx
+5 −4
@@ -2,12 +2,13 @@ import Link from 'next/link'; | ||
| 2 | 2 | import type { FocusType } from '@/lib/graph-model'; |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | − * "Explore in graph →" link for entity pages. `ref` is the public reference used by `/graph?focus=`: | |
| 6 | − * cancer slug, gene symbol, variant slug, drug slug or NCT id. | |
| 5 | + * "Explore in graph →" link for entity pages. `entityRef` is the public reference used by | |
| 6 | + * `/graph?focus=`: cancer slug, gene symbol, variant slug, drug slug or NCT id. (Named `entityRef` | |
| 7 | + * because `ref` is reserved by React and cannot be passed to a server component.) | |
| 7 | 8 | */ |
| 8 | −export function GraphLink({ type, ref, label = 'Explore in graph →', className = '' }: { type: FocusType; ref: string; label?: string; className?: string }) { | |
| 9 | +export function GraphLink({ type, entityRef, label = 'Explore in graph →', className = '' }: { type: FocusType; entityRef: string; label?: string; className?: string }) { | |
| 9 | 10 | return ( |
| 10 | − <Link href={`/graph?focus=${type}:${encodeURIComponent(ref)}`} className={`ci-link inline-flex items-center gap-1 text-[13px]${className ? ` ${className}` : ''}`} title="Open the contextual knowledge graph around this entity"> | |
| 11 | + <Link href={`/graph?focus=${type}:${encodeURIComponent(entityRef)}`} className={`ci-link inline-flex items-center gap-1 text-[13px]${className ? ` ${className}` : ''}`} title="Open the contextual knowledge graph around this entity"> | |
| 11 | 12 | {label} |
| 12 | 13 | </Link> |
| 13 | 14 | ); |
| 14 | 15 | |