Skip to content

Commit 2802693

Browse files
fix: sync bundled dashboard templates + fix scrollable detail lists
The dashboard-site/ source files were updated but never copied to src/Spectra.CLI/Dashboard/Templates/ (the embedded resources). This caused regenerated dashboards to use stale templates missing the criteria unit label fix, search filter, and scroll improvements. All coverage sections now have scrollable detail lists (max-height 400px with overflow-y: auto). Lists with >20 items get a search filter. Version 1.32.5.
1 parent ef0971d commit 2802693

File tree

4 files changed

+79
-13
lines changed

4 files changed

+79
-13
lines changed

src/Spectra.CLI/Dashboard/Templates/scripts/app.js

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -924,7 +924,7 @@ function renderThreeSectionCoverage(summary) {
924924
// Coverage sections (progress bars + detail lists)
925925
html += '<div class="coverage-sections">';
926926
html += renderCoverageSection('Documentation Coverage', summary.documentation, 'documents', renderDocDetails);
927-
html += renderCoverageSection('Acceptance Criteria Coverage', summary.acceptance_criteria, 'acceptance_criteria', renderCriteriaDetails);
927+
html += renderCoverageSection('Acceptance Criteria Coverage', summary.acceptance_criteria, 'criteria', renderCriteriaDetails);
928928
html += renderCoverageSection('Automation Coverage', summary.automation, 'tests', renderAutoDetails);
929929
html += '</div>';
930930

@@ -1354,7 +1354,15 @@ function renderCoverageSection(label, sectionData, unit, renderDetailsFn) {
13541354
html += emptyHtml;
13551355
} else if (sectionData.details && sectionData.details.length > 0) {
13561356
// Expandable detail list
1357-
html += `<button class="coverage-toggle-btn" onclick="toggleCoverageDetails('${sectionId}')">Show details</button>`;
1357+
const detailCount = sectionData.details.length;
1358+
html += `<button class="coverage-toggle-btn" onclick="toggleCoverageDetails('${sectionId}')">Show details (${detailCount})</button>`;
1359+
// Add search filter for large lists
1360+
if (detailCount > 20) {
1361+
html += `<div class="coverage-detail-filter collapsed" id="filter-${sectionId}">
1362+
<input type="text" placeholder="Filter by ID or text..." oninput="filterCoverageDetails('${sectionId}', this.value)" />
1363+
<span class="filter-count" id="filter-count-${sectionId}">${detailCount} items</span>
1364+
</div>`;
1365+
}
13581366
html += `<ul class="coverage-detail-list collapsed" id="details-${sectionId}">`;
13591367
html += renderDetailsFn(sectionData);
13601368
html += '</ul>';
@@ -1384,7 +1392,7 @@ function getEmptyState(label, sectionData, total, pct) {
13841392
return `<div class="coverage-empty-state">
13851393
<span class="empty-icon">&#9432;</span>
13861394
<div class="empty-text"><strong>No acceptance criteria tracked yet.</strong>
1387-
Add a <code>criteria</code> field to test YAML frontmatter, or create a <code>_criteria_index.yaml</code> file in your docs directory.</div>
1395+
Add a <code>criteria</code> field to test YAML frontmatter, or run <code>spectra ai analyze --extract-criteria</code> to extract from documentation.</div>
13881396
</div>`;
13891397
}
13901398
return null;
@@ -1409,15 +1417,44 @@ function getEmptyState(label, sectionData, total, pct) {
14091417
*/
14101418
function toggleCoverageDetails(sectionId) {
14111419
const list = document.getElementById('details-' + sectionId);
1412-
const btn = list?.previousElementSibling;
1413-
if (!list || !btn) return;
1420+
const filter = document.getElementById('filter-' + sectionId);
1421+
if (!list) return;
1422+
1423+
// Find the toggle button — it's the sibling before the filter (if present) or the list
1424+
const btn = (filter || list).previousElementSibling;
1425+
const count = list.children.length;
14141426

14151427
if (list.classList.contains('collapsed')) {
14161428
list.classList.remove('collapsed');
1417-
btn.textContent = 'Hide details';
1429+
if (filter) filter.classList.remove('collapsed');
1430+
if (btn) btn.textContent = 'Hide details';
14181431
} else {
14191432
list.classList.add('collapsed');
1420-
btn.textContent = 'Show details';
1433+
if (filter) filter.classList.add('collapsed');
1434+
if (btn) btn.textContent = `Show details (${count})`;
1435+
}
1436+
}
1437+
1438+
/**
1439+
* Filter coverage detail list items by search text.
1440+
*/
1441+
function filterCoverageDetails(sectionId, query) {
1442+
const list = document.getElementById('details-' + sectionId);
1443+
const countEl = document.getElementById('filter-count-' + sectionId);
1444+
if (!list) return;
1445+
1446+
const q = query.toLowerCase().trim();
1447+
let visible = 0;
1448+
1449+
for (const li of list.children) {
1450+
const text = li.textContent.toLowerCase();
1451+
const show = !q || text.includes(q);
1452+
li.style.display = show ? '' : 'none';
1453+
if (show) visible++;
1454+
}
1455+
1456+
if (countEl) {
1457+
countEl.textContent = q ? `${visible} of ${list.children.length}` : `${list.children.length} items`;
14211458
}
14221459
}
14231460

@@ -1449,8 +1486,12 @@ function renderCriteriaDetails(section) {
14491486
? '<span class="detail-icon coverage-green">&#10003;</span>'
14501487
: '<span class="detail-icon coverage-red">&#10007;</span>';
14511488
const testList = d.tests && d.tests.length > 0 ? d.tests.join(', ') : 'none';
1452-
html += `<li>
1453-
<span class="detail-name" title="${escapeHtml(d.title || d.id)}">${escapeHtml(d.id)}: ${escapeHtml(d.title || '')}</span>
1489+
const titleText = d.title || d.text || '';
1490+
const displayName = titleText
1491+
? `<strong>${escapeHtml(d.id)}</strong> ${escapeHtml(titleText)}`
1492+
: `<strong>${escapeHtml(d.id)}</strong>`;
1493+
html += `<li title="${escapeHtml(titleText)}">
1494+
<span class="detail-name">${displayName}</span>
14541495
<span class="detail-meta">${testList}</span>
14551496
${icon}
14561497
</li>`;

src/Spectra.CLI/Dashboard/Templates/styles/main.css

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1320,17 +1320,42 @@ details.result-row[open] summary::before {
13201320
padding: 0;
13211321
margin: 0.75rem 0 0 0;
13221322
font-size: 0.85rem;
1323-
max-height: 500px;
1324-
overflow: hidden;
1323+
max-height: 400px;
1324+
overflow-y: auto;
1325+
overflow-x: hidden;
13251326
transition: max-height 0.3s ease, opacity 0.3s ease;
13261327
}
13271328

13281329
.coverage-detail-list.collapsed {
13291330
max-height: 0;
1331+
overflow: hidden;
13301332
opacity: 0;
13311333
margin: 0;
13321334
}
13331335

1336+
.coverage-detail-filter {
1337+
display: flex;
1338+
align-items: center;
1339+
gap: 0.5rem;
1340+
margin: 0.5rem 0;
1341+
}
1342+
1343+
.coverage-detail-filter input {
1344+
flex: 1;
1345+
padding: 0.35rem 0.6rem;
1346+
border: 1px solid var(--border-color);
1347+
border-radius: 4px;
1348+
font-size: 0.8rem;
1349+
background: var(--bg-primary, #fff);
1350+
color: var(--text-primary);
1351+
}
1352+
1353+
.coverage-detail-filter .filter-count {
1354+
font-size: 0.75rem;
1355+
color: var(--text-muted);
1356+
white-space: nowrap;
1357+
}
1358+
13341359
.coverage-detail-list li {
13351360
padding: 0.4rem 0.5rem;
13361361
border-bottom: 1px solid var(--border-color);

src/Spectra.CLI/Spectra.CLI.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
<PackAsTool>true</PackAsTool>
2828
<ToolCommandName>spectra</ToolCommandName>
2929
<PackageId>Spectra.CLI</PackageId>
30-
<Version>1.32.4</Version>
30+
<Version>1.32.5</Version>
3131
<Authors>Spectra</Authors>
3232
<Description>AI-native test generation and management CLI</Description>
3333
<PackageOutputPath>./nupkg</PackageOutputPath>

src/Spectra.MCP/Spectra.MCP.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<PackAsTool>true</PackAsTool>
1111
<ToolCommandName>spectra-mcp</ToolCommandName>
1212
<PackageId>Spectra.MCP</PackageId>
13-
<Version>1.32.4</Version>
13+
<Version>1.32.5</Version>
1414
<Authors>Spectra</Authors>
1515
<Description>Spectra MCP Server for test execution</Description>
1616
<PackageOutputPath>./nupkg</PackageOutputPath>

0 commit comments

Comments
 (0)