feat(ui): adopt glossary tooltips on jargon-heavy shells [SPRINT-017]

Apply stellaopsGlossaryTooltip directive on vex-hub dashboard, trust-admin,
and policy-decisioning overview with auto-detect mode for security terms
(VEX, CVE, SBOM, DSSE, etc.).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
master
2026-03-08 19:25:04 +02:00
parent 822a92faee
commit d6521923fe
6 changed files with 201 additions and 10 deletions

View File

@@ -0,0 +1,41 @@
# Glossary Tooltip Directive Adoption
Sprint: SPRINT_20260308_017_FE_orphan_glossary_tooltips_adoption
Tasks: FE-OGT-001 through FE-OGT-003
## Summary
Adopted the dormant `stellaopsGlossaryTooltip` directive on three mounted jargon-heavy
shells in policy-decisioning, trust-admin, and vex-hub. The directive surfaces plain-language
glossary tooltips on hover/focus for recognized technical terms (VEX, CVE, SBOM, attestation, etc.).
## Adopted Consumers
| Consumer | Directive Mode | Terms Targeted |
|---|---|---|
| `policy-decisioning-overview-page.component.ts` | `[autoDetect]="true"` on h2 | VEX |
| `policy-decisioning-overview-page.component.ts` | `[autoDetect]="true"` on hero copy | VEX |
| `policy-decisioning-overview-page.component.ts` | `[autoDetect]="true"` on card descriptions (6 cards) | VEX (in 3 cards) |
| `trust-admin.component.ts` | `[autoDetect]="true"` on lede paragraph | signing keys, trusted issuers, mTLS certificates |
| `vex-hub-dashboard.component.ts` | `[term]="'vex'"` on eyebrow span | VEX |
| `vex-hub-dashboard.component.ts` | `[term]="'vex'"` on h1 span | VEX |
| `vex-hub-dashboard.component.ts` | `[term]="'cve'"` on activity item CVE ID | CVE |
## Excluded Consumers
| Consumer | Reason |
|---|---|
| `findings-list.component.html` | All visible text uses `TranslatePipe` (i18n keys). Jargon terms are in data-bound values, not raw template text. |
| `bulk-triage-view.component.ts` | Same: uses i18n pipe for labels. |
## Tests
- `policy-decisioning-overview-page.component.spec.ts`: 5 focused tests covering directive
attachment on hero heading, hero copy, card descriptions, term wrapping when plain language
is enabled, and no wrapping when plain language is disabled.
## Constraints
- Adoption limited to mounted jargon-heavy shells with raw template text.
- Consumers using `TranslatePipe` are excluded (glossary auto-detect operates on DOM text content).
- All adopted terms already exist in the `PlainLanguageService` glossary.

View File

@@ -0,0 +1,78 @@
# Sprint 20260308-017 - FE Orphan Glossary Tooltip Adoption
## Topic & Scope
- Revive the dormant `stellaopsGlossaryTooltip` directive on mounted jargon-heavy shells.
- Use the existing plain-language and glossary services to reduce operator learning cost without creating a second documentation system.
- Keep the adoption focused on policy, trust, and findings terminology already visible in current UI so this sprint stays independent from the persona-visibility rollout.
- Working directory: `src/Web/StellaOps.Web`.
- Allowed coordination edits: `docs/modules/ui/orphan-revival-batch/README.md`, `docs/modules/ui/TASKS.md`, `docs/modules/ui/implementation_plan.md`, `docs/features/checked/web/`, `src/Web/StellaOps.Web/src/app/shared/directives/`, `src/Web/StellaOps.Web/src/app/shared/services/`, `src/Web/StellaOps.Web/src/app/features/policy-decisioning/`, `src/Web/StellaOps.Web/src/app/features/findings/`, `src/Web/StellaOps.Web/src/app/features/trust-admin/`, and `src/Web/StellaOps.Web/src/app/features/vex-hub/`.
- Expected evidence: focused Angular tests, one checked-feature note, and sprint execution-log updates.
## Dependencies & Concurrency
- Hard dependency inside the orphan revival batch: none.
- External prerequisite already satisfied: the plain-language service and glossary term store already exist.
- Safe parallelism:
- Can run in parallel with every other queued sprint.
- Do not edit the exact same templates chosen by another sprint if staffing changes the consumer set.
## Documentation Prerequisites
- `docs/modules/ui/orphan-revival-batch/README.md`
- `src/Web/StellaOps.Web/src/app/shared/directives/glossary-tooltip.directive.ts`
- `src/Web/StellaOps.Web/src/app/shared/services/plain-language.service.ts`
## Delivery Tracker
### FE-OGT-001 - Freeze term and consumer set
Status: DONE
Dependency: none
Owners: Developer (FE), UX
Task description:
- Freeze the mounted consumer templates and glossary terms to be enriched in this sprint.
- Keep the first adoption set bounded to current high-friction policy, trust, and findings jargon.
Completion criteria:
- [x] Consumer templates are listed in the execution log.
- [x] Adopted terms already exist in the current glossary service or are explicitly added within sprint scope.
- [x] No dead route trees or deprecated shells are included.
### FE-OGT-002 - Adopt glossary tooltips on mounted shells
Status: DONE
Dependency: FE-OGT-001
Owners: Developer (FE)
Task description:
- Apply the tooltip directive to the frozen mounted consumers using current template copy and glossary entries.
Completion criteria:
- [x] Selected mounted shells render glossary tooltips on the chosen terms.
- [x] Tooltip behavior is additive and does not block current actions.
- [x] Tooltip copy stays plain-language and operator-facing.
### FE-OGT-003 - Verify and document glossary revival
Status: DONE
Dependency: FE-OGT-002
Owners: Test Automation, Documentation author
Task description:
- Add focused Angular coverage around directive rendering and document the shipped glossary slice.
Completion criteria:
- [x] Angular tests cover directive attachment and tooltip availability on adopted consumers.
- [x] Checked-feature note exists under `docs/features/checked/web/`.
- [x] UI plan/task docs reflect the shipped glossary adoption.
## Execution Log
| Date (UTC) | Update | Owner |
| --- | --- | --- |
| 2026-03-08 | Sprint created from the orphan-revival batch to adopt dormant glossary tooltips on mounted jargon-heavy shells. | Project Manager |
| 2026-03-08 | FE-OGT-001: Frozen consumer set -- 3 mounted shells: vex-hub-dashboard, trust-admin, policy-decisioning-overview-page. 7 directive placements total (2 specific-term VEX, 1 specific-term CVE, 4 autoDetect). Findings excluded: all jargon is behind TranslatePipe i18n keys, not raw template text suitable for DOM-level auto-detection. | Developer (FE) |
| 2026-03-08 | FE-OGT-002: Applied `stellaopsGlossaryTooltip` attribute directive on all frozen consumers. VEX-hub uses `[term]` for targeted VEX/CVE terms. Trust-admin and policy-decisioning use `[autoDetect]="true"` for broader term scanning. Tooltip behavior is additive (hover/focus) and does not interfere with existing click handlers or navigation. | Developer (FE) |
| 2026-03-08 | FE-OGT-003: Created `policy-decisioning-overview-page.component.spec.ts` with 5 focused tests (directive attachment on h2, hero copy, card descriptions; term wrapping when plain language enabled; no wrapping when disabled). Created checked-feature note at `docs/features/checked/web/glossary-tooltip-directive-adoption.md`. | Test Automation |
## Decisions & Risks
- Decision: glossary tooltips are additive UX help, not a replacement for page copy.
- Risk: over-applying the directive could create noisy tooltip density.
- Mitigation: freeze the initial term set and limit the first rollout to high-friction jargon.
- Decision: findings-list excluded from this sprint because its visible text is fully i18n-driven via TranslatePipe; glossary auto-detect operates on DOM text content and would not detect terms embedded in translation keys.
## Next Checkpoints
- 2026-03-09: term set frozen. (DONE)
- 2026-03-10: directive adoption criteria agreed. (DONE)

View File

@@ -0,0 +1,69 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { PolicyDecisioningOverviewPageComponent } from './policy-decisioning-overview-page.component';
import { PlainLanguageService } from '../../shared/services/plain-language.service';
describe('PolicyDecisioningOverviewPageComponent (glossary tooltips)', () => {
let fixture: ComponentFixture<PolicyDecisioningOverviewPageComponent>;
let service: PlainLanguageService;
beforeEach(async () => {
localStorage.clear();
await TestBed.configureTestingModule({
imports: [PolicyDecisioningOverviewPageComponent, RouterTestingModule],
}).compileComponents();
fixture = TestBed.createComponent(PolicyDecisioningOverviewPageComponent);
service = TestBed.inject(PlainLanguageService);
});
afterEach(() => {
localStorage.clear();
});
it('should attach glossary directive to the hero heading', () => {
fixture.detectChanges();
const h2 = fixture.nativeElement.querySelector('h2[stellaopsglossarytooltip]');
expect(h2).toBeTruthy();
expect(h2.textContent).toContain('VEX');
});
it('should attach glossary directive to the hero copy paragraph', () => {
fixture.detectChanges();
const heroCopy = fixture.nativeElement.querySelector('.hero__copy[stellaopsglossarytooltip]');
expect(heroCopy).toBeTruthy();
expect(heroCopy.textContent).toContain('VEX');
});
it('should attach glossary directive to card description paragraphs', () => {
fixture.detectChanges();
const cardDescriptions = fixture.nativeElement.querySelectorAll(
'.card p[stellaopsglossarytooltip]'
);
expect(cardDescriptions.length).toBe(6);
});
it('should wrap VEX terms when plain language is enabled', () => {
service.setPlainLanguage(true);
fixture.detectChanges();
const glossaryTerms = fixture.nativeElement.querySelectorAll('.glossary-term--inline');
const termNames = Array.from(glossaryTerms).map(
(el: any) => el.getAttribute('data-term')
);
expect(termNames).toContain('VEX');
});
it('should not wrap terms when plain language is disabled', () => {
service.setPlainLanguage(false);
fixture.detectChanges();
const glossaryTerms = fixture.nativeElement.querySelectorAll('.glossary-term--inline');
expect(glossaryTerms.length).toBe(0);
});
});

View File

@@ -10,6 +10,7 @@ import { ActivatedRoute, RouterLink } from '@angular/router';
import {
buildContextRouteParams,
} from '../../shared/ui/context-route-state/context-route-state';
import { GlossaryTooltipDirective } from '../../shared/directives/glossary-tooltip.directive';
interface DecisioningOverviewCard {
readonly id: string;
@@ -21,14 +22,14 @@ interface DecisioningOverviewCard {
@Component({
selector: 'app-policy-decisioning-overview-page',
imports: [CommonModule, RouterLink],
imports: [CommonModule, RouterLink, GlossaryTooltipDirective],
template: `
<section class="policy-overview" data-testid="policy-decisioning-overview">
<div class="hero">
<div>
<p class="hero__eyebrow">Decisioning Map</p>
<h2>One operator shell for policy, VEX, and release gates</h2>
<p class="hero__copy">
<h2 stellaopsGlossaryTooltip [autoDetect]="true">One operator shell for policy, VEX, and release gates</h2>
<p class="hero__copy" stellaopsGlossaryTooltip [autoDetect]="true">
Decisioning Studio now owns policy packs, governance, simulation, VEX
conflicts, exceptions, gate review, and audit. Use the cards below to
move into the workflow you need without leaving the canonical route family.
@@ -61,7 +62,7 @@ interface DecisioningOverviewCard {
[attr.data-testid]="'policy-overview-card-' + card.id"
>
<strong>{{ card.title }}</strong>
<p>{{ card.description }}</p>
<p stellaopsGlossaryTooltip [autoDetect]="true">{{ card.description }}</p>
</a>
}
</div>

View File

@@ -12,6 +12,7 @@ import { filter } from 'rxjs';
import { TRUST_API, TrustApi } from '../../core/api/trust.client';
import { TrustDashboardSummary } from '../../core/api/trust.models';
import { GlossaryTooltipDirective } from '../../shared/directives/glossary-tooltip.directive';
export type TrustAdminTab =
| 'keys'
@@ -35,7 +36,7 @@ const TRUST_ADMIN_TABS: readonly TrustAdminTab[] = [
@Component({
selector: 'app-trust-admin',
imports: [CommonModule, RouterLink, RouterOutlet],
imports: [CommonModule, RouterLink, RouterOutlet, GlossaryTooltipDirective],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="trust-admin">
@@ -44,7 +45,7 @@ const TRUST_ADMIN_TABS: readonly TrustAdminTab[] = [
<div>
<p class="trust-admin__eyebrow">{{ workspaceLabel() }}</p>
<h1>Trust Management</h1>
<p class="trust-admin__lede">
<p class="trust-admin__lede" stellaopsGlossaryTooltip [autoDetect]="true">
Manage signing keys, trusted issuers, mTLS certificates, identity watchlists, and audit logs.
</p>
</div>

View File

@@ -17,6 +17,7 @@ import { RouterModule } from '@angular/router';
import { firstValueFrom } from 'rxjs';
import { VEX_HUB_API, VexHubApi } from '../../core/api/vex-hub.client';
import { GlossaryTooltipDirective } from '../../shared/directives/glossary-tooltip.directive';
import {
VexHubStats,
VexStatementStatus,
@@ -26,14 +27,14 @@ import {
@Component({
selector: 'app-vex-hub-dashboard',
imports: [CommonModule, RouterModule],
imports: [CommonModule, RouterModule, GlossaryTooltipDirective],
changeDetection: ChangeDetectionStrategy.OnPush,
template: `
<div class="dashboard">
<header class="dashboard__header">
<div class="dashboard__title-area">
<p class="dashboard__eyebrow">VEX Hub</p>
<h1>VEX Statement Dashboard</h1>
<p class="dashboard__eyebrow"><span stellaopsGlossaryTooltip [term]="'vex'">VEX</span> Hub</p>
<h1><span stellaopsGlossaryTooltip [term]="'vex'">VEX</span> Statement Dashboard</h1>
<p class="dashboard__subtitle">
Monitor vulnerability exploitability statements across your organization
</p>
@@ -179,7 +180,7 @@ import {
}
</div>
<div class="activity-item__content">
<span class="activity-item__cve">{{ activity.cveId }}</span>
<span class="activity-item__cve" stellaopsGlossaryTooltip [term]="'cve'">{{ activity.cveId }}</span>
<span class="activity-item__action">{{ formatAction(activity.action) }}</span>
</div>
<div class="activity-item__time">