From 38f47924044897c606a813d715d0e7781352ec34 Mon Sep 17 00:00:00 2001 From: "Tom Meschter (from Dev Box)" Date: Fri, 17 Jul 2026 11:23:21 -0700 Subject: [PATCH 1/3] feat: link to open issues from each skill dashboard page Add a 'View open issues' link to each skill's detail page on the Skills dashboard. The link opens a GitHub issue search filtered to open issues labelled with the skill name (issue #2870). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 07e3ca5a-25a5-4116-bf5b-cf60473bad7a --- dashboard/src/skills/App.tsx | 21 ++++++++++++++++ .../src/skills/__tests__/issuesUrl.test.ts | 24 +++++++++++++++++++ dashboard/src/skills/skills.css | 14 +++++++++++ 3 files changed, 59 insertions(+) create mode 100644 dashboard/src/skills/__tests__/issuesUrl.test.ts diff --git a/dashboard/src/skills/App.tsx b/dashboard/src/skills/App.tsx index b1e619a01..ee33be057 100644 --- a/dashboard/src/skills/App.tsx +++ b/dashboard/src/skills/App.tsx @@ -20,6 +20,18 @@ import { /** Number of trailing days shown in every graph. */ const WINDOW_DAYS = 10; +/** Repository whose issues are surfaced from each skill page. */ +const ISSUES_REPO = "microsoft/GitHub-Copilot-for-Azure"; + +/** + * Build a GitHub search URL for the open issues labelled with a skill's name. + * The skill name is used verbatim as the label value. + */ +export function issuesUrl(skillName: string): string { + const query = `is:issue state:open label:"${skillName}"`; + return `https://github.com/${ISSUES_REPO}/issues?q=${encodeURIComponent(query)}`; +} + /** A plugin skill with its description, as surfaced by the frontmatter collector. */ interface Skill { name: string; @@ -254,6 +266,15 @@ export default function App() {

Description length: {selectedSkill.descriptionLength} characters

+

+ + View open issues for {selectedSkill.name} ↗ + +

diff --git a/dashboard/src/skills/__tests__/issuesUrl.test.ts b/dashboard/src/skills/__tests__/issuesUrl.test.ts new file mode 100644 index 000000000..d6eeaccbf --- /dev/null +++ b/dashboard/src/skills/__tests__/issuesUrl.test.ts @@ -0,0 +1,24 @@ +import { describe, it, expect } from "vitest"; +import { issuesUrl } from "../App"; + +describe("issuesUrl", () => { + it("points at the repo's issue search", () => { + expect(issuesUrl("azure-deploy")).toContain( + "https://github.com/microsoft/GitHub-Copilot-for-Azure/issues?q=", + ); + }); + + it("filters to open issues labelled with the skill name", () => { + const url = issuesUrl("azure-deploy"); + const query = new URL(url).searchParams.get("q"); + expect(query).toBe('is:issue state:open label:"azure-deploy"'); + }); + + it("url-encodes skill names with special characters", () => { + const url = issuesUrl("foundry agent/create"); + // Raw skill name must not appear unencoded in the URL. + expect(url).not.toContain("foundry agent/create"); + const query = new URL(url).searchParams.get("q"); + expect(query).toBe('is:issue state:open label:"foundry agent/create"'); + }); +}); diff --git a/dashboard/src/skills/skills.css b/dashboard/src/skills/skills.css index bfe8cfaf9..ad1557979 100644 --- a/dashboard/src/skills/skills.css +++ b/dashboard/src/skills/skills.css @@ -83,6 +83,20 @@ font-size: 0.9rem; } +.skills-issues-link { + margin: 0.5rem 0 0; + font-size: 0.9rem; +} + +.skills-issues-link a { + color: var(--color-focus, #3b82f6); + text-decoration: none; +} + +.skills-issues-link a:hover { + text-decoration: underline; +} + .skills-tests-heading { margin: 1.5rem 0 0.75rem; font-size: 1.15rem; From 8af459f69e6b7d56316d0b3b595025f110a2f059 Mon Sep 17 00:00:00 2001 From: "Tom Meschter (from Dev Box)" Date: Mon, 20 Jul 2026 10:41:28 -0700 Subject: [PATCH 2/3] refactor: address PR review feedback on skill issues link - Move issuesUrl/ISSUES_REPO into src/skills/issuesUrl.ts so tests import without pulling in the React/recharts UI module - Add a title hint that the issues link opens in a new tab - Add a :focus-visible style for keyboard accessibility Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 07e3ca5a-25a5-4116-bf5b-cf60473bad7a --- dashboard/src/skills/App.tsx | 14 ++------------ dashboard/src/skills/__tests__/issuesUrl.test.ts | 2 +- dashboard/src/skills/issuesUrl.ts | 11 +++++++++++ dashboard/src/skills/skills.css | 7 +++++++ 4 files changed, 21 insertions(+), 13 deletions(-) create mode 100644 dashboard/src/skills/issuesUrl.ts diff --git a/dashboard/src/skills/App.tsx b/dashboard/src/skills/App.tsx index a432a060e..d8e22ad9c 100644 --- a/dashboard/src/skills/App.tsx +++ b/dashboard/src/skills/App.tsx @@ -8,6 +8,7 @@ import { ResponsiveContainer, } from "recharts"; import { apiUrl } from "../shared/apiUrl"; +import { issuesUrl } from "./issuesUrl"; import { buildDaySeries, groupByTest, @@ -20,18 +21,6 @@ import { /** Number of trailing days shown in every graph. */ const WINDOW_DAYS = 10; -/** Repository whose issues are surfaced from each skill page. */ -const ISSUES_REPO = "microsoft/GitHub-Copilot-for-Azure"; - -/** - * Build a GitHub search URL for the open issues labelled with a skill's name. - * The skill name is used verbatim as the label value. - */ -export function issuesUrl(skillName: string): string { - const query = `is:issue state:open label:"${skillName}"`; - return `https://github.com/${ISSUES_REPO}/issues?q=${encodeURIComponent(query)}`; -} - /** A plugin skill with its description, as surfaced by the frontmatter collector. */ interface Skill { name: string; @@ -281,6 +270,7 @@ export default function App() { href={issuesUrl(selectedSkill.name)} target="_blank" rel="noopener noreferrer" + title={`Open issues for ${selectedSkill.name} in a new tab`} > View open issues for {selectedSkill.name} ↗ diff --git a/dashboard/src/skills/__tests__/issuesUrl.test.ts b/dashboard/src/skills/__tests__/issuesUrl.test.ts index d6eeaccbf..9fa243e8a 100644 --- a/dashboard/src/skills/__tests__/issuesUrl.test.ts +++ b/dashboard/src/skills/__tests__/issuesUrl.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from "vitest"; -import { issuesUrl } from "../App"; +import { issuesUrl } from "../issuesUrl"; describe("issuesUrl", () => { it("points at the repo's issue search", () => { diff --git a/dashboard/src/skills/issuesUrl.ts b/dashboard/src/skills/issuesUrl.ts new file mode 100644 index 000000000..2bf32f3b3 --- /dev/null +++ b/dashboard/src/skills/issuesUrl.ts @@ -0,0 +1,11 @@ +/** Repository whose issues are surfaced from each skill page. */ +export const ISSUES_REPO = "microsoft/GitHub-Copilot-for-Azure"; + +/** + * Build a GitHub search URL for the open issues labelled with a skill's name. + * The skill name is used verbatim as the label value. + */ +export function issuesUrl(skillName: string): string { + const query = `is:issue state:open label:"${skillName}"`; + return `https://github.com/${ISSUES_REPO}/issues?q=${encodeURIComponent(query)}`; +} diff --git a/dashboard/src/skills/skills.css b/dashboard/src/skills/skills.css index 463a8ee78..749e91be5 100644 --- a/dashboard/src/skills/skills.css +++ b/dashboard/src/skills/skills.css @@ -97,6 +97,13 @@ text-decoration: underline; } +.skills-issues-link a:focus-visible { + outline: 2px solid var(--color-focus, #3b82f6); + outline-offset: 2px; + text-decoration: underline; + border-radius: 2px; +} + .skills-file-count { margin: 0; color: var(--color-text-muted); From 565565c2001241d1f07f784f14a4eb0a2b94897f Mon Sep 17 00:00:00 2001 From: "Tom Meschter (from Dev Box)" Date: Mon, 20 Jul 2026 14:02:21 -0700 Subject: [PATCH 3/3] refactor: make ISSUES_REPO module-private in issuesUrl.ts It is only referenced within issuesUrl.ts, so it does not need to be exported. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 07e3ca5a-25a5-4116-bf5b-cf60473bad7a --- dashboard/src/skills/issuesUrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/src/skills/issuesUrl.ts b/dashboard/src/skills/issuesUrl.ts index 2bf32f3b3..ff3eb9c12 100644 --- a/dashboard/src/skills/issuesUrl.ts +++ b/dashboard/src/skills/issuesUrl.ts @@ -1,5 +1,5 @@ /** Repository whose issues are surfaced from each skill page. */ -export const ISSUES_REPO = "microsoft/GitHub-Copilot-for-Azure"; +const ISSUES_REPO = "microsoft/GitHub-Copilot-for-Azure"; /** * Build a GitHub search URL for the open issues labelled with a skill's name.