Skip to content

Commit e5a8ad0

Browse files
authored
Fix navigation and table scrolling (#53)
* Fix navigation and table scrolling behavior - Replace router.push with next/link for proper cmd-click support - Make table headers sticky (top: 0) with proper z-index layering - Add max-height and overflow to DetailedMatrix for fixed viewport scrolling - Wrap SummaryTable rows in Link components for accessible navigation - Update KernelCard to accept href prop and wrap in Link when provided - Remove useRouter dependency from content components * Fix React key warning and tier header scrolling - Import Fragment from React and use with key prop - Remove colSpan from tier headers, render individual cells instead - Tier labels stay sticky left while background cells scroll properly * Use solid background for tier headers to prevent transparency bleed
1 parent 3abc31d commit e5a8ad0

File tree

4 files changed

+50
-46
lines changed

4 files changed

+50
-46
lines changed

site/app/components/CardsContent.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use client';
22

3-
import { useRouter } from 'next/navigation';
43
import { Github, Table2, Grid3X3, LayoutGrid } from 'lucide-react';
54
import { TooltipProvider } from '@/components/ui/tooltip';
65
import { Header } from '@/components/Header';
@@ -12,7 +11,6 @@ interface CardsContentProps {
1211
}
1312

1413
export function CardsContent({ data }: CardsContentProps) {
15-
const router = useRouter();
1614

1715
return (
1816
<TooltipProvider>
@@ -52,9 +50,7 @@ export function CardsContent({ data }: CardsContentProps) {
5250
<KernelCard
5351
key={report.kernel_name}
5452
report={report}
55-
onClick={() => {
56-
router.push(`/kernel/${encodeURIComponent(report.kernel_name)}/`);
57-
}}
53+
href={`/kernel/${encodeURIComponent(report.kernel_name)}/`}
5854
/>
5955
))}
6056
</div>

site/app/components/HomeContent.tsx

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
'use client';
22

3-
import { useRouter } from 'next/navigation';
43
import { Github, Table2, Grid3X3, LayoutGrid } from 'lucide-react';
54
import { TooltipProvider } from '@/components/ui/tooltip';
65
import { Header } from '@/components/Header';
@@ -12,7 +11,6 @@ interface HomeContentProps {
1211
}
1312

1413
export function HomeContent({ data }: HomeContentProps) {
15-
const router = useRouter();
1614

1715
return (
1816
<TooltipProvider>
@@ -47,12 +45,7 @@ export function HomeContent({ data }: HomeContentProps) {
4745
</nav>
4846

4947
{/* Summary table */}
50-
<SummaryTable
51-
matrix={data}
52-
onKernelClick={(name) => {
53-
router.push(`/kernel/${encodeURIComponent(name)}/`);
54-
}}
55-
/>
48+
<SummaryTable matrix={data} />
5649
</div>
5750
</main>
5851

site/src/components/ConformanceMatrix.tsx

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
'use client';
22

3+
import { Fragment } from 'react';
4+
import Link from 'next/link';
35
import {
46
Table,
57
TableBody,
@@ -15,7 +17,6 @@ import { getAllTestNames, getPassedCount, getTotalCount, getTierScore, TIER_DESC
1517

1618
interface ConformanceMatrixProps {
1719
matrix: MatrixType;
18-
onKernelClick?: (kernelName: string) => void;
1920
}
2021

2122
const TIERS: TestCategory[] = ['tier1_basic', 'tier2_interactive', 'tier3_rich_output', 'tier4_advanced'];
@@ -29,7 +30,7 @@ function getScoreColor(percentage: number): string {
2930
}
3031

3132
/** Summary table showing tier scores for each kernel */
32-
export function SummaryTable({ matrix, onKernelClick }: ConformanceMatrixProps) {
33+
export function SummaryTable({ matrix }: ConformanceMatrixProps) {
3334
return (
3435
<div className="rounded-lg border border-ctp-surface0 overflow-x-auto bg-ctp-mantle">
3536
<Table>
@@ -51,39 +52,43 @@ export function SummaryTable({ matrix, onKernelClick }: ConformanceMatrixProps)
5152
const total = getTotalCount(report);
5253
const percentage = total > 0 ? Math.round((passed / total) * 100) : 0;
5354
const LanguageIcon = getLanguageIcon(report.kernel_name, report.language);
55+
const href = `/kernel/${encodeURIComponent(report.kernel_name)}/`;
5456

5557
return (
5658
<TableRow
5759
key={report.kernel_name}
58-
className={`border-ctp-surface0 ${onKernelClick ? 'cursor-pointer hover:bg-ctp-surface0/50' : ''}`}
59-
onClick={() => onKernelClick?.(report.kernel_name)}
60+
className="border-ctp-surface0 hover:bg-ctp-surface0/50"
6061
>
61-
<TableCell className="font-medium pl-4">
62-
<div className="flex items-center gap-2.5">
62+
<TableCell className="font-medium pl-4 p-0">
63+
<Link href={href} className="flex items-center gap-2.5 py-2 px-4">
6364
<LanguageIcon className="h-5 w-5 flex-shrink-0" />
6465
<div>
6566
<div className="text-ctp-text">{report.kernel_name}</div>
6667
<div className="text-xs text-ctp-subtext0">{report.implementation}</div>
6768
</div>
68-
</div>
69+
</Link>
6970
</TableCell>
70-
<TableCell className="text-center font-mono text-sm text-ctp-lavender">
71-
{report.protocol_version}
71+
<TableCell className="text-center font-mono text-sm text-ctp-lavender p-0">
72+
<Link href={href} className="block py-2 px-4">{report.protocol_version}</Link>
7273
</TableCell>
7374
{TIERS.map((tier) => {
7475
const [tierPassed, tierTotal] = getTierScore(report, tier);
7576
const tierPercent = tierTotal > 0 ? Math.round((tierPassed / tierTotal) * 100) : 0;
7677
return (
77-
<TableCell key={tier} className={`text-center font-mono text-sm ${getScoreColor(tierPercent)}`}>
78-
{tierTotal > 0 ? `${tierPassed}/${tierTotal}` : '-'}
78+
<TableCell key={tier} className={`text-center font-mono text-sm ${getScoreColor(tierPercent)} p-0`}>
79+
<Link href={href} className="block py-2 px-4">
80+
{tierTotal > 0 ? `${tierPassed}/${tierTotal}` : '-'}
81+
</Link>
7982
</TableCell>
8083
);
8184
})}
82-
<TableCell className="text-center">
83-
<div className={`font-mono ${getScoreColor(percentage)}`}>
84-
{passed}/{total}
85-
</div>
86-
<div className={`text-xs ${getScoreColor(percentage)}`}>{percentage}%</div>
85+
<TableCell className="text-center p-0">
86+
<Link href={href} className="block py-2 px-4">
87+
<div className={`font-mono ${getScoreColor(percentage)}`}>
88+
{passed}/{total}
89+
</div>
90+
<div className={`text-xs ${getScoreColor(percentage)}`}>{percentage}%</div>
91+
</Link>
8792
</TableCell>
8893
</TableRow>
8994
);
@@ -118,15 +123,15 @@ export function DetailedMatrix({ matrix }: ConformanceMatrixProps) {
118123
}
119124

120125
return (
121-
<div className="rounded-lg border border-ctp-surface0 overflow-x-auto bg-ctp-mantle">
126+
<div className="rounded-lg border border-ctp-surface0 overflow-auto bg-ctp-mantle max-h-[calc(100vh-280px)]">
122127
<Table>
123-
<TableHeader>
128+
<TableHeader className="sticky top-0 z-20 bg-ctp-mantle">
124129
<TableRow className="border-ctp-surface0 hover:bg-transparent">
125-
<TableHead className="min-w-[200px] sticky left-0 bg-ctp-mantle text-ctp-subtext0">Test</TableHead>
130+
<TableHead className="min-w-[200px] sticky left-0 z-30 bg-ctp-mantle text-ctp-subtext0">Test</TableHead>
126131
{matrix.reports.map((report) => {
127132
const LanguageIcon = getLanguageIcon(report.kernel_name, report.language);
128133
return (
129-
<TableHead key={report.kernel_name} className="text-center min-w-[80px] text-ctp-subtext0 py-3">
134+
<TableHead key={report.kernel_name} className="text-center min-w-[80px] text-ctp-subtext0 py-3 bg-ctp-mantle">
130135
<div className="flex flex-col items-center gap-1.5">
131136
<LanguageIcon className="h-5 w-5" />
132137
<span className="text-xs">{report.kernel_name}</span>
@@ -142,20 +147,20 @@ export function DetailedMatrix({ matrix }: ConformanceMatrixProps) {
142147
if (testsInTier.length === 0) return null;
143148

144149
return (
145-
<>
150+
<Fragment key={tier}>
146151
{/* Tier header row */}
147-
<TableRow key={`${tier}-header`} className="bg-ctp-surface0/30 hover:bg-ctp-surface0/30 border-ctp-surface0">
148-
<TableCell
149-
colSpan={matrix.reports.length + 1}
150-
className="font-semibold text-xs uppercase tracking-wide sticky left-0 bg-ctp-surface0/30 text-ctp-mauve"
151-
>
152+
<TableRow className="bg-ctp-surface0 hover:bg-ctp-surface0 border-ctp-surface0">
153+
<TableCell className="font-semibold text-xs uppercase tracking-wide sticky left-0 z-10 bg-ctp-surface0 text-ctp-mauve">
152154
{TIER_DESCRIPTIONS[tier]}
153155
</TableCell>
156+
{matrix.reports.map((report) => (
157+
<TableCell key={report.kernel_name} className="bg-ctp-surface0" />
158+
))}
154159
</TableRow>
155160
{/* Test rows */}
156161
{testsInTier.map((testName) => (
157162
<TableRow key={testName} className="border-ctp-surface0 hover:bg-ctp-surface0/30">
158-
<TableCell className="font-mono text-xs sticky left-0 bg-ctp-mantle text-ctp-text">
163+
<TableCell className="font-mono text-xs sticky left-0 z-10 bg-ctp-mantle text-ctp-text">
159164
{testName}
160165
</TableCell>
161166
{matrix.reports.map((report) => {
@@ -172,7 +177,7 @@ export function DetailedMatrix({ matrix }: ConformanceMatrixProps) {
172177
})}
173178
</TableRow>
174179
))}
175-
</>
180+
</Fragment>
176181
);
177182
})}
178183
</TableBody>

site/src/components/KernelCard.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
'use client';
22

3+
import Link from 'next/link';
34
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
45
import { Badge } from '@/components/ui/badge';
56
import { Network } from 'lucide-react';
@@ -9,7 +10,7 @@ import { getPassedCount, getTotalCount, getTierScore, TIER_DESCRIPTIONS } from '
910

1011
interface KernelCardProps {
1112
report: KernelReport;
12-
onClick?: () => void;
13+
href?: string;
1314
}
1415

1516
const TIERS: TestCategory[] = ['tier1_basic', 'tier2_interactive', 'tier3_rich_output', 'tier4_advanced'];
@@ -29,16 +30,15 @@ function getProgressColor(percentage: number): string {
2930
return 'bg-ctp-red';
3031
}
3132

32-
export function KernelCard({ report, onClick }: KernelCardProps) {
33+
export function KernelCard({ report, href }: KernelCardProps) {
3334
const passed = getPassedCount(report);
3435
const total = getTotalCount(report);
3536
const percentage = total > 0 ? Math.round((passed / total) * 100) : 0;
3637
const LanguageIcon = getLanguageIcon(report.kernel_name, report.language);
3738

38-
return (
39+
const cardContent = (
3940
<Card
40-
className={`bg-ctp-mantle border-ctp-surface0 ${onClick ? 'cursor-pointer hover:border-ctp-mauve/50 hover:shadow-lg hover:shadow-ctp-mauve/5 transition-all' : ''}`}
41-
onClick={onClick}
41+
className={`bg-ctp-mantle border-ctp-surface0 ${href ? 'cursor-pointer hover:border-ctp-mauve/50 hover:shadow-lg hover:shadow-ctp-mauve/5 transition-all' : ''}`}
4242
>
4343
<CardHeader className="pb-2">
4444
<div className="flex items-start justify-between">
@@ -100,4 +100,14 @@ export function KernelCard({ report, onClick }: KernelCardProps) {
100100
</CardContent>
101101
</Card>
102102
);
103+
104+
if (href) {
105+
return (
106+
<Link href={href} className="block">
107+
{cardContent}
108+
</Link>
109+
);
110+
}
111+
112+
return cardContent;
103113
}

0 commit comments

Comments
 (0)