From e014b1661cdb859064dc0550f2471f8dac0d0986 Mon Sep 17 00:00:00 2001 From: Renhe Li Date: Mon, 20 Jul 2026 11:55:55 +0800 Subject: [PATCH 1/3] Add name filter to can-i-use dashboard Add a search box below the coverage overview that filters scenarios by name (case-insensitive substring match) across all tiers and tables. Tables with no matching scenarios are hidden and a 'No scenarios match' message is shown when nothing matches. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cae584f3-c6db-4366-9331-811f932cbfea --- .../src/components/dashboard.module.css | 14 +++++++ .../src/components/dashboard.tsx | 40 ++++++++++++++++--- 2 files changed, 49 insertions(+), 5 deletions(-) diff --git a/packages/spec-dashboard/src/components/dashboard.module.css b/packages/spec-dashboard/src/components/dashboard.module.css index 0078383db0e..dd08fd6fd4d 100644 --- a/packages/spec-dashboard/src/components/dashboard.module.css +++ b/packages/spec-dashboard/src/components/dashboard.module.css @@ -22,3 +22,17 @@ .tier-filter { margin-bottom: 20px; } + +.name-filter { + margin-bottom: 20px; +} + +.name-filter-input { + width: 320px; + max-width: 100%; +} + +.no-results { + display: block; + margin: 10px 5px; +} diff --git a/packages/spec-dashboard/src/components/dashboard.tsx b/packages/spec-dashboard/src/components/dashboard.tsx index 62f15ff51a6..c8f729bd2b1 100644 --- a/packages/spec-dashboard/src/components/dashboard.tsx +++ b/packages/spec-dashboard/src/components/dashboard.tsx @@ -1,5 +1,5 @@ -import { Card, CardHeader, Text } from "@fluentui/react-components"; -import { FunctionComponent, useState } from "react"; +import { Card, CardHeader, SearchBox, Text } from "@fluentui/react-components"; +import { FunctionComponent, useMemo, useState } from "react"; import { CoverageSummary } from "../apis.js"; import { useTierFiltering } from "../hooks/use-tier-filtering.js"; import { TierConfig } from "../utils/tier-filtering-utils.js"; @@ -25,6 +25,7 @@ export const Dashboard: FunctionComponent = ({ emitterDisplayNames, }) => { const [selectedTier, setSelectedTier] = useState(undefined); + const [nameFilter, setNameFilter] = useState(""); const { filteredSummaries, allTiers } = useTierFiltering( coverageSummaries, @@ -32,8 +33,25 @@ export const Dashboard: FunctionComponent = ({ selectedTier, ); - const summaryTables = filteredSummaries - .filter((s) => !selectedTier || s.manifest.scenarios.length > 0) + const normalizedNameFilter = nameFilter.trim().toLowerCase(); + + const searchedSummaries = useMemo(() => { + if (!normalizedNameFilter) { + return filteredSummaries; + } + return filteredSummaries.map((summary) => ({ + ...summary, + manifest: { + ...summary.manifest, + scenarios: summary.manifest.scenarios.filter((s) => + s.name.toLowerCase().includes(normalizedNameFilter), + ), + }, + })); + }, [filteredSummaries, normalizedNameFilter]); + + const summaryTables = searchedSummaries + .filter((s) => (!selectedTier && !normalizedNameFilter) || s.manifest.scenarios.length > 0) .map((coverageSummary, i) => (
= ({ )}
{specsCardTable}
- {summaryTables} +
+ setNameFilter(data.value)} + /> +
+ {normalizedNameFilter && summaryTables.length === 0 ? ( + No scenarios match "{nameFilter.trim()}". + ) : ( + summaryTables + )}
); }; From 91d3f99c9a2708b590d3461739bec860f4a58f20 Mon Sep 17 00:00:00 2001 From: Renhe Li Date: Tue, 21 Jul 2026 13:17:55 +0800 Subject: [PATCH 2/3] Auto-expand matching scenarios when filtering by name When a name filter is active, expand all rows so matching scenarios are immediately visible without manual expansion. Use React's useDeferredValue so the SearchBox stays responsive while the filter + expand render happens as a lower-priority, non-blocking update. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: cae584f3-c6db-4366-9331-811f932cbfea --- .../src/components/dashboard-table.tsx | 12 ++++++++---- .../spec-dashboard/src/components/dashboard.tsx | 14 +++++++++++--- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/packages/spec-dashboard/src/components/dashboard-table.tsx b/packages/spec-dashboard/src/components/dashboard-table.tsx index d511d7facc9..6297973a909 100644 --- a/packages/spec-dashboard/src/components/dashboard-table.tsx +++ b/packages/spec-dashboard/src/components/dashboard-table.tsx @@ -14,12 +14,15 @@ import { ManifestTreeNode, TreeTableRow } from "./tree-table/types.js"; export interface DashboardTableProps { coverageSummary: CoverageSummary; emitterDisplayNames?: Record; + /** When true, all tree rows are expanded regardless of user toggles. */ + expandAll?: boolean; } function buildTreeRows( node: ManifestTreeNode, expandedRows: Record, toggleExpand: (key: string) => void, + expandAll = false, depth = 0, ): TreeTableRow[] { const rows: TreeTableRow[] = []; @@ -30,7 +33,7 @@ function buildTreeRows( const hasChildren = Boolean(child.children && Object.keys(child.children).length > 0); const key = child.fullName; - const expanded = expandedRows[key] ?? false; + const expanded = expandAll || (expandedRows[key] ?? false); rows.push({ key, item: child, @@ -41,7 +44,7 @@ function buildTreeRows( toggleExpand: () => toggleExpand(key), }); if (hasChildren && expanded) { - for (const row of buildTreeRows(child, expandedRows, toggleExpand, depth + 1)) { + for (const row of buildTreeRows(child, expandedRows, toggleExpand, expandAll, depth + 1)) { rows.push(row); } } @@ -56,6 +59,7 @@ function buildTreeRows( export const DashboardTable: FunctionComponent = ({ coverageSummary, emitterDisplayNames, + expandAll, }) => { const languages: string[] = Object.keys(coverageSummary.generatorReports) as any; const tree = useMemo(() => createTree(coverageSummary.manifest), [coverageSummary.manifest]); @@ -70,8 +74,8 @@ export const DashboardTable: FunctionComponent = ({ [setExpandedRows], ); const treeRows = useMemo(() => { - return buildTreeRows(tree, expandedRows, toggleExpand); - }, [tree, expandedRows, toggleExpand]); + return buildTreeRows(tree, expandedRows, toggleExpand, expandAll); + }, [tree, expandedRows, toggleExpand, expandAll]); const rows = treeRows.map((x) => { return ( diff --git a/packages/spec-dashboard/src/components/dashboard.tsx b/packages/spec-dashboard/src/components/dashboard.tsx index c8f729bd2b1..f08d681435c 100644 --- a/packages/spec-dashboard/src/components/dashboard.tsx +++ b/packages/spec-dashboard/src/components/dashboard.tsx @@ -1,5 +1,5 @@ import { Card, CardHeader, SearchBox, Text } from "@fluentui/react-components"; -import { FunctionComponent, useMemo, useState } from "react"; +import { FunctionComponent, useDeferredValue, useMemo, useState } from "react"; import { CoverageSummary } from "../apis.js"; import { useTierFiltering } from "../hooks/use-tier-filtering.js"; import { TierConfig } from "../utils/tier-filtering-utils.js"; @@ -27,13 +27,18 @@ export const Dashboard: FunctionComponent = ({ const [selectedTier, setSelectedTier] = useState(undefined); const [nameFilter, setNameFilter] = useState(""); + // Defer the filter value so typing stays responsive: the SearchBox updates + // instantly while the expensive filtering + tree expansion render happens on + // a lower-priority, non-blocking update. + const deferredNameFilter = useDeferredValue(nameFilter); + const { filteredSummaries, allTiers } = useTierFiltering( coverageSummaries, scenarioTierConfig, selectedTier, ); - const normalizedNameFilter = nameFilter.trim().toLowerCase(); + const normalizedNameFilter = deferredNameFilter.trim().toLowerCase(); const searchedSummaries = useMemo(() => { if (!normalizedNameFilter) { @@ -57,6 +62,7 @@ export const Dashboard: FunctionComponent = ({ )); @@ -91,7 +97,9 @@ export const Dashboard: FunctionComponent = ({ /> {normalizedNameFilter && summaryTables.length === 0 ? ( - No scenarios match "{nameFilter.trim()}". + + No scenarios match "{deferredNameFilter.trim()}". + ) : ( summaryTables )} From d19f19abc3e41eb038702137ba6672f1e25d5415 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 13 Aug 2026 09:25:17 +0000 Subject: [PATCH 3/3] style(spec-dashboard): fix dashboard.tsx formatting Co-authored-by: lirenhe <9100546+lirenhe@users.noreply.github.com> --- packages/spec-dashboard/src/components/dashboard.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/spec-dashboard/src/components/dashboard.tsx b/packages/spec-dashboard/src/components/dashboard.tsx index 0a9524c13a0..4c61858f546 100644 --- a/packages/spec-dashboard/src/components/dashboard.tsx +++ b/packages/spec-dashboard/src/components/dashboard.tsx @@ -1,7 +1,7 @@ import { Card, CardHeader, SearchBox, Text } from "@fluentui/react-components"; import type { FunctionComponent } from "react"; -import type { CoverageSummary } from "../apis.js"; import { useDeferredValue, useMemo, useState } from "react"; +import type { CoverageSummary } from "../apis.js"; import { useTierFiltering } from "../hooks/use-tier-filtering.js"; import type { TierConfig } from "../utils/tier-filtering-utils.js"; import { CoverageOverview } from "./coverage-overview.js";