SPB Git forge

spb/cancerindex

Public
37commits 1branches 0releases
2.9 MBsize
maindefault branch
10 days agolast push
TypeScript 97.2% SQL 1.5% CSS 0.6% JavaScript 0.5%

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>
Simon-Pierre Boucher committed 13 days ago (Sep 11, 2026) parent 1f3bdf3

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