diff --git a/frontend-modern/src/components/shared/Toggle.test.tsx b/frontend-modern/src/components/shared/Toggle.test.tsx index 124c8994b6..59d44609ed 100644 --- a/frontend-modern/src/components/shared/Toggle.test.tsx +++ b/frontend-modern/src/components/shared/Toggle.test.tsx @@ -62,4 +62,23 @@ describe('Toggle', () => { expect(button).toHaveAttribute('aria-describedby'); expect(button).toHaveAccessibleDescription('Turns it on'); }); + + it('supports an external visible label when the layout owns label placement', () => { + render(() => ( + <> + Alert-triggered patrols + Run when alerts fire. + + + )); + + const button = screen.getByRole('button', { name: 'Alert-triggered patrols' }); + expect(button).toHaveAttribute('aria-labelledby', 'external-toggle-label'); + expect(button).toHaveAttribute('aria-describedby', 'external-toggle-description'); + expect(button).toHaveAccessibleDescription('Run when alerts fire.'); + }); }); diff --git a/frontend-modern/src/components/shared/Toggle.tsx b/frontend-modern/src/components/shared/Toggle.tsx index bd2c588bf6..9eecef11c1 100644 --- a/frontend-modern/src/components/shared/Toggle.tsx +++ b/frontend-modern/src/components/shared/Toggle.tsx @@ -59,8 +59,8 @@ export function Toggle(props: LabeledToggleProps) { {(props.label || props.description) && ( diff --git a/frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx b/frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx index b6ae9b8874..5ab7075673 100644 --- a/frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx +++ b/frontend-modern/src/features/patrol/PatrolIntelligenceHeader.tsx @@ -1,4 +1,4 @@ -import { createMemo, For, Show } from 'solid-js'; +import { createMemo, createUniqueId, For, Show } from 'solid-js'; import RefreshCwIcon from 'lucide-solid/icons/refresh-cw'; import PlayIcon from 'lucide-solid/icons/play'; import CircleHelpIcon from 'lucide-solid/icons/circle-help'; @@ -42,6 +42,13 @@ export function getPatrolConfigurationFailureInlineDetails( export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState }) { const state = props.state; + const fieldIdPrefix = `patrol-config-${createUniqueId()}`; + const fieldIds = { + alertTriggeredAnalysis: `${fieldIdPrefix}-alert-triggered-analysis`, + patrolAlertTriggers: `${fieldIdPrefix}-alert-triggered-patrols`, + patrolAnomalyTriggers: `${fieldIdPrefix}-anomaly-triggered-patrols`, + autonomousCriticalRemediation: `${fieldIdPrefix}-autonomous-critical-remediation`, + }; const headerMeta = getPatrolPageHeaderMeta(); const scheduleOptions = createMemo(() => buildPatrolScheduleOptions(state.patrolInterval())); const selectedScheduleLabel = createMemo( @@ -325,9 +332,12 @@ export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState
- +

Analyze infrastructure automatically when critical alerts fire.

@@ -338,6 +348,7 @@ export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState state.handleAlertTriggeredAnalysisChange(e.currentTarget.checked) } disabled={state.isUpdatingSettings() || state.alertAnalysisLocked()} + ariaLabelledBy={fieldIds.alertTriggeredAnalysis} />
@@ -365,9 +376,12 @@ export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState
- +

Run scoped Patrol checks when alerts fire or clear.

@@ -378,14 +392,18 @@ export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState state.handlePatrolAlertTriggersChange(e.currentTarget.checked) } disabled={state.isUpdatingSettings() || !state.patrolEnabledLocal()} + ariaLabelledBy={fieldIds.patrolAlertTriggers} />
- +

Run scoped Patrol checks when learned baselines detect high-signal anomalies. @@ -397,14 +415,18 @@ export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState state.handlePatrolAnomalyTriggersChange(e.currentTarget.checked) } disabled={state.isUpdatingSettings() || !state.patrolEnabledLocal()} + ariaLabelledBy={fieldIds.patrolAnomalyTriggers} />

- +

Permit Patrol to execute critical remediation actions without approval.

@@ -418,6 +440,7 @@ export function PatrolIntelligenceHeader(props: { state: PatrolIntelligenceState state.autonomyLevel() === 'assisted' || state.autonomyLevel() === 'full' ) } + ariaLabelledBy={fieldIds.autonomousCriticalRemediation} />
diff --git a/frontend-modern/src/pages/__tests__/AIIntelligence.test.tsx b/frontend-modern/src/pages/__tests__/AIIntelligence.test.tsx index 65627e942c..cfa8ef5e10 100644 --- a/frontend-modern/src/pages/__tests__/AIIntelligence.test.tsx +++ b/frontend-modern/src/pages/__tests__/AIIntelligence.test.tsx @@ -274,10 +274,14 @@ vi.mock('@/components/shared/Toggle', () => ({ disabled?: boolean; onToggle?: (value: boolean) => void; ariaLabel?: string; + ariaLabelledBy?: string; + ariaDescribedBy?: string; }) => (