diff --git a/web/src/components/CatalogCharts.test.js b/web/src/components/CatalogCharts.test.js index 36d99e2..53022e6 100644 --- a/web/src/components/CatalogCharts.test.js +++ b/web/src/components/CatalogCharts.test.js @@ -5,6 +5,7 @@ import CatalogCharts from "./CatalogCharts.svelte"; const repos = [ { name: "cais", + html_url: "https://github.com/puppe1990/cais", language: "Go", stargazers_count: 2, traffic: { @@ -19,6 +20,7 @@ const repos = [ }, { name: "atlas", + html_url: "https://github.com/puppe1990/atlas", language: "Python", stargazers_count: 40, traffic: { @@ -41,10 +43,10 @@ describe("CatalogCharts", () => { test("shows top unique visitors and unique cloners", () => { render(CatalogCharts, { props: { repos } }); const visitors = screen.getByRole("region", { name: "Top unique visitors" }); - expect(within(visitors).getByText("cais")).toBeInTheDocument(); + expect(within(visitors).getByRole("link", { name: "cais" })).toHaveAttribute("href", "https://github.com/puppe1990/cais"); expect(within(visitors).getByText("70")).toBeInTheDocument(); const cloners = screen.getByRole("region", { name: "Top unique cloners" }); - expect(within(cloners).getByText("atlas")).toBeInTheDocument(); + expect(within(cloners).getByRole("link", { name: "atlas" })).toHaveAttribute("href", "https://github.com/puppe1990/atlas"); expect(within(cloners).getByText("60")).toBeInTheDocument(); }); @@ -55,6 +57,7 @@ describe("CatalogCharts", () => { expect(screen.getByRole("region", { name: "Unique visitors by language" })).toBeInTheDocument(); expect(screen.getByRole("region", { name: "Last 14 days" })).toBeInTheDocument(); const quiet = screen.getByRole("region", { name: "Quiet stars" }); - expect(within(quiet).getByText("cais")).toBeInTheDocument(); + expect(within(quiet).getByRole("link", { name: "cais" })).toHaveAttribute("href", "https://github.com/puppe1990/cais"); + expect(within(screen.getByRole("region", { name: "Unique visitors by language" })).queryByRole("link")).not.toBeInTheDocument(); }); }); diff --git a/web/src/components/RankChart.svelte b/web/src/components/RankChart.svelte index b242f46..a04afbf 100644 --- a/web/src/components/RankChart.svelte +++ b/web/src/components/RankChart.svelte @@ -18,7 +18,11 @@
    {#each rows as row}
  1. - {row[nameKey]} + {#if row.href} + {row[nameKey]} + {:else} + {row[nameKey]} + {/if} diff --git a/web/src/lib/trafficCharts.js b/web/src/lib/trafficCharts.js index e830efe..f7843b8 100644 --- a/web/src/lib/trafficCharts.js +++ b/web/src/lib/trafficCharts.js @@ -11,6 +11,7 @@ export function rankTraffic(repos, key, limit = 10) { const rows = trafficRepos(repos) .map((repo) => ({ name: repo.name, + href: repo.html_url || "", language: repo.language || "Not specified", stars: repo.stargazers_count || 0, value: Number(repo.traffic?.[key]) || 0, @@ -113,7 +114,7 @@ export function quietStarsRank(repos, limit = 10) { .map((repo) => { const uniques = repo.traffic.view_uniques || 0; const stars = repo.stargazers_count || 0; - return { name: repo.name, uniques, stars, score: uniques / Math.max(stars, 1) }; + return { name: repo.name, href: repo.html_url || "", uniques, stars, score: uniques / Math.max(stars, 1) }; }) .filter((row) => row.uniques > 0) .sort((a, b) => b.score - a.score || b.uniques - a.uniques) diff --git a/web/src/lib/trafficCharts.test.js b/web/src/lib/trafficCharts.test.js index 9ae8b60..ba2cfd9 100644 --- a/web/src/lib/trafficCharts.test.js +++ b/web/src/lib/trafficCharts.test.js @@ -68,6 +68,12 @@ describe("rankTraffic", () => { ]); }); + test("keeps the repository html url on ranked rows", () => { + const withUrls = catalog.map((repo, i) => ({ ...repo, html_url: `https://github.com/octocat/${repo.name}` })); + expect(rankTraffic(withUrls, "view_uniques", 1)[0].href).toBe("https://github.com/octocat/beta"); + expect(quietStarsRank(withUrls)[0].href).toBe("https://github.com/octocat/beta"); + }); + test("scales bar share against the peak value", () => { const [lead, second] = rankTraffic(catalog, "view_uniques", 10); expect(lead.share).toBe(100); diff --git a/web/src/pages/Home.test.js b/web/src/pages/Home.test.js index 89ea02f..e5d3daa 100644 --- a/web/src/pages/Home.test.js +++ b/web/src/pages/Home.test.js @@ -120,8 +120,9 @@ describe("Home", () => { await fireEvent.click(screen.getByRole("tab", { name: "Charts" })); expect(screen.getByRole("tab", { name: "Charts" })).toHaveAttribute("aria-selected", "true"); expect(screen.getByRole("region", { name: "Top unique visitors" })).toBeInTheDocument(); - expect(screen.queryByRole("link", { name: "hello-world" })).not.toBeInTheDocument(); + expect(screen.queryByRole("tabpanel", { name: "Catalog" })).not.toBeInTheDocument(); await fireEvent.click(screen.getByRole("tab", { name: "Catalog" })); + expect(screen.getByRole("tabpanel", { name: "Catalog" })).toBeInTheDocument(); expect(screen.getByRole("link", { name: "hello-world" })).toBeInTheDocument(); }); diff --git a/web/static/css/atlas.css b/web/static/css/atlas.css index 8322174..c316739 100644 --- a/web/static/css/atlas.css +++ b/web/static/css/atlas.css @@ -1265,6 +1265,14 @@ a.stat:hover { font-family: var(--font-mono); font-size: 0.78rem; color: var(--ink-dim); + text-decoration: none; +} + +a.rank-name:hover, +a.rank-name:focus-visible { + color: var(--copper-hot); + text-decoration: underline; + outline: none; } .rank-track {