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,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">