Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion pages/dashboard/PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@
- `src/components/data-state.js` — presentation-only data-state metrics card grid for availability, completeness, and freshness.
- `src/components/table-region.js` — presentation-only reusable table wrapper for repeated table-region markup, header rows, empty-state rows, and keyed body-row descriptors across built-in and custom tables.
- `src/components/summary-copy.js` — presentation-only shared singular/plural summary-count copy for repeated item-count text surfaces.
- `src/components/view-chrome.js` — presentation-only reusable page-section, titled-region, summary-list/summary-region, summary-style definition-list and definition-list rows, generic custom-view chrome paragraphs, custom-view source/metadata/context chrome, shared custom-view section chrome, custom-view state-message/details helpers, and built-in provenance-section helpers.
- `src/components/view-chrome.js` — presentation-only reusable page-section, titled-region, metadata-section, summary-list/summary-region, summary-style definition-list and definition-list rows, generic custom-view chrome paragraphs, custom-view source/metadata/context chrome, shared custom-view section chrome, custom-view state-message/details helpers, and built-in provenance-section helpers.
- `src/components/workflow-topology.js` — topology overview section for package and standalone workflow structure, now reusing shared definition-list rows for its summary metrics.
- `src/components/ui-primitives.js` — presentation-only shared section-heading, vital-stat, and UTC date-time helpers for repeated dashboard component chrome, including optional summary-free heading shells.
- `src/components/count-formatters.js` — presentation-only shared count and singular/plural text helpers for repeated dashboard status, workflow, and signal copy.
Expand All @@ -132,6 +132,15 @@

## Run log

### 2026-08-31 (metadata-section helper refactor)
- Re-inventoried repeated UI construction under `pages/dashboard/src/` and selected the next bounded presentation-only DOM slice from the queue: repeated titled metadata `<section><h2>…</h2>…</section>` assembly in `src/components/outcome-detail.js`, with the same section-chrome family already housed in `src/components/view-chrome.js`.
- Extracted `renderMetadataSection(...)` into `src/components/view-chrome.js` with a minimal composable API for section title, content node, and optional heading tag, then replaced every identified duplicate call site in `src/components/outcome-detail.js`.
- Collapsed the local outcome-detail metadata-section helper so the outcome sidebar now reuses shared section chrome without changing heading text, link labels, DOM text, or class names.
- Added unit coverage in `test/unit/view-chrome.test.js` for default and custom heading levels while preserving the existing `test/unit/outcome-detail.test.js` assertions that lock the affected metadata headings, badges, links, and sanitized Markdown behavior.
- Proved unchanged behavior by rendering the affected outcome-detail route fixture through `pages/dashboard/scripts/check-outcome-detail-render.mjs` after the refactor and confirming the refactor was structural-only, while the existing outcome-detail unit assertions remained unchanged and green.
- Verified `pages/dashboard/` quality gates in this run: `npm install`, `npm run build`, `npm run typecheck`, `npm run lint`, and `npm test` all pass. Playwright was not rerun for this bounded helper extraction because no browser-only behavior changed; the current browser-gate status remains tracked in `PLAN.md` infrastructure blockers.
- Next candidates in the queue: extract the repeated definitions/observations dual-region composition in `src/presenter.js`; extract the repeated summary-plus-trend section composition in `src/presenter.js`; extract the next shared pure helpers around subject/provenance/count formatting in `src/presenter.js`.

### 2026-08-31 (section-heading shell refactor)
- Re-inventoried repeated UI construction under `pages/dashboard/src/` and selected the next bounded presentation-only DOM slice from the queue: repeated `.section-heading` shells in `src/components/package-detail.js`, `src/components/repositories-view.js`, plus the existing shared call sites in `src/components/execution-elements.js` and `src/components/ui-elements.js`.
- Generalized `src/components/ui-primitives.js` `renderSectionHeading(...)` to a minimal options object with optional summary support, then replaced every identified call site so the shared helper now renders both summary-bearing and summary-free section headers without changing classes, headings, or visible copy.
Expand Down
10 changes: 1 addition & 9 deletions pages/dashboard/src/components/outcome-detail.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { octicon } from '../octicons.js';
import { renderModeBadge, renderStatusBadge } from './badge.js';
import { findLink, renderExternalLink } from './link-content.js';
import { formatUtcDateTime } from './ui-primitives.js';
import { renderMetadataSection } from './view-chrome.js';

const ALLOWED_MARKDOWN_TAGS = new Set([
'A', 'BLOCKQUOTE', 'BR', 'CODE', 'DEL', 'DETAILS', 'DIV', 'EM',
Expand Down Expand Up @@ -144,15 +145,6 @@ function renderOutcome(outcome) {
);
}

/**
* @param {string} title
* @param {Node} content
* @returns {HTMLElement}
*/
function renderMetadataSection(title, content) {
return h('section', null, h('h2', null, title), content);
}

/**
* Rebuilds GitHub-rendered Markdown through a strict element and attribute allowlist.
* @param {string} html
Expand Down
10 changes: 10 additions & 0 deletions pages/dashboard/src/components/view-chrome.js
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,16 @@ export function renderProvenanceSection(pageId, items) {
return renderTitledRegion(pageId, 'Provenance', renderProvenanceList(items));
}

/**
* @param {string} title
* @param {Node} content
* @param {'h2'|'h3'|'h4'} [headingTag]
* @returns {HTMLElement}
*/
export function renderMetadataSection(title, content, headingTag = 'h2') {
return h('section', null, h(headingTag, null, title), content);
}

/**
* @param {string} value
* @returns {string}
Expand Down
3 changes: 2 additions & 1 deletion pages/dashboard/test/e2e/smoke.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,8 @@ function buildPresenterModuleUrl() {
.replace("'../octicons.js'", JSON.stringify(octiconsModuleUrl))
.replace("'./badge.js'", JSON.stringify(badgeModuleUrl))
.replace("'./link-content.js'", JSON.stringify(linkContentModuleUrl))
.replace("'./ui-primitives.js'", JSON.stringify(uiPrimitivesModuleUrl));
.replace("'./ui-primitives.js'", JSON.stringify(uiPrimitivesModuleUrl))
.replace("'./view-chrome.js'", JSON.stringify(viewChromeModuleUrl));
const outcomeDetailModuleUrl = `data:text/javascript;charset=utf-8,${encodeURIComponent(outcomeDetailSource)}`;

const executionElementsSource = readFileSync(new URL('../../src/components/execution-elements.js', import.meta.url), 'utf8')
Expand Down
14 changes: 13 additions & 1 deletion pages/dashboard/test/unit/view-chrome.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @vitest-environment jsdom
import { describe, expect, it } from 'vitest';
import { customViewAvailabilityMessage, renderContextChrome, renderContextList, renderCustomViewStateDetails, renderDefinitionList, renderDefinitionListRows, renderPageSection, renderProvenanceList, renderProvenanceSection, renderSummaryList, renderSummaryRegion, renderTitledRegion, renderViewChrome, renderViewHeader, renderViewSectionChrome } from '../../src/components/view-chrome.js';
import { customViewAvailabilityMessage, renderContextChrome, renderContextList, renderCustomViewStateDetails, renderDefinitionList, renderDefinitionListRows, renderMetadataSection, renderPageSection, renderProvenanceList, renderProvenanceSection, renderSummaryList, renderSummaryRegion, renderTitledRegion, renderViewChrome, renderViewHeader, renderViewSectionChrome } from '../../src/components/view-chrome.js';

describe('view chrome component helpers', () => {
it('DLS-SAFE-007 renders focusable labeled page sections with deterministic heading ids', () => {
Expand Down Expand Up @@ -199,4 +199,16 @@ describe('view chrome component helpers', () => {
expect(section.getAttribute('aria-labelledby')).toBe('evals-provenance-heading');
expect(section.querySelector('.provenance-list')?.textContent).toContain('evals: evals-fixture (fixture) — as of 2026-08-29T20:00:00Z');
});

it('renders metadata sections with configurable heading levels', () => {
const defaultHeading = renderMetadataSection('Status', document.createElement('p'));
defaultHeading.querySelector('p')?.append('Closed');
const customHeading = renderMetadataSection('Workflow', document.createElement('p'), 'h3');
customHeading.querySelector('p')?.append('Daily review');

expect(defaultHeading.querySelector('h2')?.textContent).toBe('Status');
expect(defaultHeading.textContent).toContain('Closed');
expect(customHeading.querySelector('h3')?.textContent).toBe('Workflow');
expect(customHeading.textContent).toContain('Daily review');
});
});