diff --git a/docs/implplan/SPRINT_20260308_027_FE_page_header_context_header_derivation.md b/docs/implplan/SPRINT_20260308_027_FE_page_header_context_header_derivation.md index 0c7c68fd1..f73329d7a 100644 --- a/docs/implplan/SPRINT_20260308_027_FE_page_header_context_header_derivation.md +++ b/docs/implplan/SPRINT_20260308_027_FE_page_header_context_header_derivation.md @@ -20,7 +20,7 @@ ## Delivery Tracker ### FE-PHD-001 - Freeze the canonical header contract -Status: TODO +Status: DONE Dependency: none Owners: UX, Developer (FE) Task description: @@ -28,12 +28,12 @@ Task description: - Document which capabilities remain mandatory: contextual eyebrow, chips, back action, action slot strategy, supportive note, and responsive stacking behavior. Completion criteria: -- [ ] A single canonical header API is defined. -- [ ] Unused or redundant `PageHeaderComponent` behavior is either absorbed or rejected explicitly. -- [ ] Header semantics are described in UX terms, not only implementation terms. +- [x] A single canonical header API is defined. +- [x] Unused or redundant `PageHeaderComponent` behavior is either absorbed or rejected explicitly. +- [x] Header semantics are described in UX terms, not only implementation terms. ### FE-PHD-002 - Derive the reusable header primitive -Status: TODO +Status: DONE Dependency: FE-PHD-001 Owners: Developer (FE) Task description: @@ -41,12 +41,12 @@ Task description: - Keep the API small and expressive; avoid two near-identical shared header components. Completion criteria: -- [ ] The canonical header primitive supports the required title, metadata, and action variants. -- [ ] `PageHeaderComponent` is either removed or reduced to a compatibility wrapper with a clear migration path. -- [ ] Header behavior remains responsive and accessible. +- [x] The canonical header primitive supports the required title, metadata, and action variants. +- [x] `PageHeaderComponent` is either removed or reduced to a compatibility wrapper with a clear migration path. +- [x] Header behavior remains responsive and accessible. ### FE-PHD-003 - Adopt the derived header on target pages -Status: TODO +Status: DONE Dependency: FE-PHD-002 Owners: Developer (FE), UX Task description: @@ -54,33 +54,40 @@ Task description: - Use adoption to prove the pattern works for both dense operator surfaces and simpler settings/admin pages. Completion criteria: -- [ ] At least one simple settings/admin page and one richer operational page adopt the derived header pattern. -- [ ] Repeated header markup is removed from adopted surfaces. -- [ ] The adopted pages gain clearer context and action placement. +- [x] At least one simple settings/admin page and one richer operational page adopt the derived header pattern. +- [x] Repeated header markup is removed from adopted surfaces. +- [x] The adopted pages gain clearer context and action placement. ### FE-PHD-004 - Verify, document, and retire the orphan path -Status: TODO +Status: DONE Dependency: FE-PHD-003 Owners: Test Automation, Documentation author Task description: - Add focused tests for the canonical header behavior and record the derivation decision in UI docs so future reviews treat the old generic header as intentionally superseded. Completion criteria: -- [ ] Component or host tests cover the canonical header behavior. -- [ ] UI docs explain the header derivation and adoption targets. -- [ ] The old orphan path is no longer ambiguous in the shared inventory. +- [x] Component or host tests cover the canonical header behavior. +- [x] UI docs explain the header derivation and adoption targets. +- [x] The old orphan path is no longer ambiguous in the shared inventory. ## Execution Log | Date (UTC) | Update | Owner | | --- | --- | --- | | 2026-03-08 | Sprint created to derive the unused generic page header into the mounted context-header pattern and adopt one canonical header primitive. | Codex | +| 2026-03-08 | FE-PHD-001: Frozen the canonical header contract. `ContextHeaderComponent` is the single canonical header. `PageHeaderComponent` had only title, subtitle, and action slots; all useful bits absorbed. Canonical API: title (required), eyebrow (optional), subtitle (optional), contextNote (optional), chips (optional status indicators), backLabel+backClick (optional return action), headingLevel (1/2/3 for semantic HTML), testId (optional), header-actions content projection slot. | Developer (FE) | +| 2026-03-08 | FE-PHD-002: Enhanced `ContextHeaderComponent` with configurable heading level (h1/h2/h3), testId, arrow in return button, ARIA labels on return button and chip list, JSDoc on all inputs. `PageHeaderComponent` reduced to deprecated compatibility wrapper delegating to `ContextHeaderComponent`. | Developer (FE) | +| 2026-03-08 | FE-PHD-003: Adopted canonical header on 4 target pages: `RegistryAdminComponent` (admin/setup page), `PackRegistryBrowserComponent` (operational page), `DeadLetterDashboardComponent` (operational page), `OfflineKitComponent` (operational page). Removed repeated ad-hoc header markup from all 4. Each page now has eyebrow breadcrumb, consistent subtitle, and projected actions via the shared header. | Developer (FE) | +| 2026-03-08 | FE-PHD-004: Added 15 focused component tests covering title rendering, eyebrow/subtitle display, chips with ARIA roles, back action behavior, action slot projection, heading level configurability (h1/h2/h3), testId attribute, and responsive layout structure. All 15 pass. Updated sprint and docs. Marked `PageHeaderComponent` as deprecated in the shared index. | Test Automation | ## Decisions & Risks -- Decision target: one canonical header primitive, not parallel “simple” and “contextual” header abstractions. +- **Decision: Single canonical header.** `ContextHeaderComponent` is the sole canonical header primitive. `PageHeaderComponent` is deprecated to a thin compatibility wrapper. +- **Decision: Heading level configurability.** Added `headingLevel` input (1, 2, or 3) to support pages nested inside shells that already provide an h1. Default remains h1. +- **Decision: Back button arrow.** Added a left arrow indicator to the return button for improved affordance and accessibility. +- **Decision: testId support.** Added `testId` input that maps to `data-testid` on the header element for Playwright/test targeting. +- **Decision: Adopted pages.** Registry Admin (admin/setup), Pack Registry Browser (operational), Dead-Letter Dashboard (operational), Offline Kit (operational). These four prove the pattern works across both simple admin and richer operational surfaces. - Risk: overfitting the header API to too many page variants could make the primitive hard to use. -- Mitigation: validate the API on a small adoption set before broad rollout. +- Mitigation: validated the API on a bounded 4-page adoption set. Future rollout should proceed incrementally. ## Next Checkpoints -- Freeze the canonical header contract. -- Prototype the derived shared header. -- Adopt it on a bounded set of mounted pages. +- Broader rollout of canonical header to remaining pages with ad-hoc headers (not scoped to this sprint). +- Eventual removal of `PageHeaderComponent` once no references remain. diff --git a/docs/modules/ui/TASKS.md b/docs/modules/ui/TASKS.md index cfd1e01ee..d8794cd92 100644 --- a/docs/modules/ui/TASKS.md +++ b/docs/modules/ui/TASKS.md @@ -22,7 +22,7 @@ - `docs/implplan/SPRINT_20260308_022_FE_unreachable_release_investigation_routes.md` - `docs/implplan/SPRINT_20260308_023_FE_unreachable_registry_admin_route.md` - `docs/implplan/SPRINT_20260308_026_FE_settings_information_architecture_rationalization.md` -- `docs/implplan/SPRINT_20260308_027_FE_page_header_context_header_derivation.md` +- [DONE] `docs/implplan/SPRINT_20260308_027_FE_page_header_context_header_derivation.md` - Derived `PageHeaderComponent` into canonical `ContextHeaderComponent` with unified header contract, adopted on 4 target pages, 15 focused tests. - [DONE] `docs/implplan/SPRINT_20260308_028_FE_metric_card_dashboard_card_derivation.md` - Derived MetricCardComponent into canonical KPI card with semantic delta handling, severity accents, and loading/empty/error states. Adopted on 3 dashboards (12 bespoke tiles replaced). 40 tests pass. - `docs/implplan/SPRINT_20260308_029_FE_timeline_list_audit_timeline_derivation.md` - [DONE] `docs/implplan/SPRINT_20260308_030_FE_split_pane_list_detail_shell_consolidation.md` - Consolidated SplitPaneComponent into ListDetailShellComponent as the canonical master-detail layout primitive. Added collapsible toggle, detail slide-in animation, and accessibility roles. Adopted on signing-key-dashboard. SplitPaneComponent deprecated. diff --git a/docs/modules/ui/implementation_plan.md b/docs/modules/ui/implementation_plan.md index 24bd4b59c..25b38f84d 100644 --- a/docs/modules/ui/implementation_plan.md +++ b/docs/modules/ui/implementation_plan.md @@ -8,6 +8,7 @@ Provide a living plan for UI deliverables, dependencies, and evidence. - Update this file when new scoped work is approved. - Sprint `025` is active for safe cleanup of approved dead leaves and committed generated/debug artifacts in the Web workspace. - Sprint `026` shipped Settings IA rationalization: the Settings shell now owns only personal preferences (appearance, language, layout, AI assistant). All admin, tenant, and operations configuration leaves redirect to their canonical owners (Administration, Setup, Ops). See `docs/features/checked/web/settings-ia-rationalization-ui.md`. +- Sprint `027` is DONE: derived `PageHeaderComponent` into canonical `ContextHeaderComponent` with unified header contract (configurable heading level, testId, ARIA), adopted on 4 target pages (RegistryAdmin, PackRegistryBrowser, DeadLetterDashboard, OfflineKit), 15 focused tests. - Sprint `031` (Witness Viewer Evidence Derivation) is DONE. Derived the orphan `WitnessViewerComponent` into 4 reusable proof-inspection sections (VerificationSummary, SignatureInspector, AttestationDetail, EvidencePayload) adopted on Reachability WitnessPage and Evidence PacketPage. See `docs/implplan/SPRINT_20260308_031_FE_witness_viewer_evidence_derivation.md`. ## Near-term deliverables diff --git a/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-dashboard.component.ts b/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-dashboard.component.ts index 1da7ff339..19ad7a925 100644 --- a/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-dashboard.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/deadletter/deadletter-dashboard.component.ts @@ -1,4 +1,5 @@ // Sprint: SPRINT_20251229_030_FE - Dead-Letter Management UI +// Sprint 027: Adopted canonical ContextHeaderComponent import { Component, OnInit, OnDestroy, inject, signal, computed } from '@angular/core'; import { RouterModule } from '@angular/router'; @@ -14,18 +15,20 @@ import { BatchReplayProgress, ERROR_CODE_REFERENCES, } from '../../core/api/deadletter.models'; +import { ContextHeaderComponent } from '../../shared/ui/context-header/context-header.component'; @Component({ selector: 'app-deadletter-dashboard', - imports: [RouterModule, FormsModule], + imports: [RouterModule, FormsModule, ContextHeaderComponent], template: `
- + @if (batchProgress()) { @@ -410,23 +413,6 @@ import { margin: 0 auto; } - .page-header { - display: flex; - justify-content: space-between; - align-items: flex-start; - margin-bottom: 1.5rem; - } - - .page-header h1 { - margin: 0; - font-size: 1.75rem; - } - - .subtitle { - margin: 0.25rem 0 0; - color: var(--color-text-secondary); - } - .header-actions { display: flex; gap: 0.5rem; diff --git a/src/Web/StellaOps.Web/src/app/features/offline-kit/offline-kit.component.ts b/src/Web/StellaOps.Web/src/app/features/offline-kit/offline-kit.component.ts index 4065b3df7..d2d0bce0c 100644 --- a/src/Web/StellaOps.Web/src/app/features/offline-kit/offline-kit.component.ts +++ b/src/Web/StellaOps.Web/src/app/features/offline-kit/offline-kit.component.ts @@ -1,35 +1,40 @@ // Offline Kit Component // Sprint 026: Offline Kit Integration +// Sprint 027: Adopted canonical ContextHeaderComponent import { Component, ChangeDetectionStrategy, inject } from '@angular/core'; import { RouterModule } from '@angular/router'; import { OfflineModeService } from '../../core/services/offline-mode.service'; +import { ContextHeaderComponent } from '../../shared/ui/context-header/context-header.component'; @Component({ selector: 'app-offline-kit', - imports: [RouterModule], + imports: [RouterModule, ContextHeaderComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: `
- + + +