Converge connection editor notices on CalloutCard

This commit is contained in:
rcourtman
2026-06-13 17:36:27 +01:00
parent cb53fd6b60
commit 4e7d7ddb4f
12 changed files with 366 additions and 83 deletions
@@ -1013,7 +1013,11 @@ class helper.
they extract the inner form bodies from the per-type panels and
render them inline under the editor while still driving the existing
`TrueNASSettingsPanelState` and `VMwareSettingsPanelState` APIs for
save, test, preview, and impact-preview behavior. The add flow
save, test, preview, and impact-preview behavior. Feature-disabled,
delete-error, and probe guidance notices in the connection editor may
compose frontend-primitives' `CalloutCard`; agent-lifecycle owns the
selected-source lifecycle semantics and form handoff, not colored local
notice shells. The add flow
must not reintroduce the full per-type workspace (Proxmox discovery
card, configured nodes table, node-modal stack; TrueNAS/VMware
connection list with headers and row actions) into the credential
@@ -406,6 +406,10 @@ payload shape change when the portal presents compact client rows.
`frontend-modern/src/components/Settings/AvailabilitySettingsPanel.tsx`,
`frontend-modern/src/components/Settings/availabilitySettingsModel.ts`,
and `AvailabilityTargetSlot.tsx` own the browser transport shape.
Availability target probe-result and error notices may compose
frontend-primitives' `CalloutCard` for shared settings callout chrome;
API contracts own the target CRUD/test payload semantics and endpoint
routing, not local colored alert shells.
Connections ledger rows with type
`availability` must route management handoffs, pause, remove, and test
actions to those availability-target endpoints and must not reuse node,
@@ -1357,6 +1357,10 @@ not a replacement status card, CTA band, or page-local nested card.
`frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts`
and
`frontend-modern/src/components/Settings/__tests__/settingsArchitecture.test.ts`.
Connection-editor status, feature-disabled, delete-error, and probe-result
notices are part of the same settings callout boundary: the editor and
credential slots own the source-specific lifecycle or API meaning, while
`CalloutCard` owns the warning/success/danger shell and compact density.
8. Keep hosted settings-shell framing imports safe for bundle initialization.
Self-hosted billing titles, descriptions, and referral copy used by
`settingsHeaderMeta.ts`, `settingsNavCatalog.ts`, and adjacent settings
@@ -3349,6 +3353,13 @@ maintaining feature-local colored bordered wrappers. The primitive owns the tone
palette and the `scale="compact"` density used by smaller settings notices, and
the `settings-callout-card-shell` shared-template registry rule requires current
settings consumers to compose it instead of reintroducing local panel shells.
Connection-editor status, feature-disabled, delete-error, and probe-result
notices are part of that same settings callout boundary: the editor and
credential slots own the source-specific lifecycle or API meaning, while
`CalloutCard` owns the warning/success/danger shell and compact density.
The `settings-connection-editor-local-*-callout-shell` pattern guards block
future connection-editor files from reintroducing amber, red, or rose local
notice shells.
Settings external documentation text links must route through
`ExternalTextLink`, while button-styled external actions route through
@@ -1558,6 +1558,7 @@
"owned_files": [
"frontend-modern/src/components/Settings/APITokenManager.tsx",
"frontend-modern/src/components/Settings/apiTokenManagerModel.ts",
"frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx",
"frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/NodeCredentialSlot.tsx",
"frontend-modern/src/components/Settings/infrastructureOperationsModel.tsx",
"frontend-modern/src/components/Settings/NodeModalAuthenticationSection.tsx",
@@ -1821,6 +1822,27 @@
"frontend-modern/src/types/api.ts"
]
},
{
"id": "availability-target-settings-client-surface",
"label": "availability target settings client proof",
"match_prefixes": [],
"match_files": [
"frontend-modern/src/api/availabilityTargets.ts",
"frontend-modern/src/components/Settings/availabilitySettingsModel.ts",
"frontend-modern/src/components/Settings/AvailabilitySettingsPanel.tsx",
"frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx",
"internal/api/availability_handlers.go"
],
"allow_same_subsystem_tests": false,
"test_prefixes": [],
"exact_files": [
"frontend-modern/src/api/__tests__/availabilityTargets.test.ts",
"frontend-modern/src/components/Settings/__tests__/AvailabilitySettingsPanel.test.tsx",
"frontend-modern/src/components/Settings/__tests__/settingsArchitecture.test.ts",
"frontend-modern/src/components/Settings/ConnectionEditor/__tests__/AvailabilityTargetSlot.test.tsx",
"internal/api/availability_handlers_test.go"
]
},
{
"id": "frontend-api-clients",
"label": "frontend API client proof",
@@ -301,6 +301,13 @@
},
"requiredConsumers": [
{ "path": "src/components/Settings/AIProviderConfigurationSection.tsx" },
{ "path": "src/components/Settings/ConnectionEditor/AddressProbeStep.tsx" },
{
"path": "src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx"
},
{
"path": "src/components/Settings/ConnectionEditor/CredentialSlots/TrueNASCredentialSlot.tsx"
},
{
"path": "src/components/Settings/ConnectionEditor/CredentialSlots/VMwareCredentialSlot.tsx"
},
@@ -316,6 +323,35 @@
"path": "src/components/Settings/AIProviderConfigurationSection.tsx",
"patterns": ["rounded border border-red-200", "bg-red-50 dark:bg-red-900 px-2 py-1.5"]
},
{
"path": "src/components/Settings/ConnectionEditor/AddressProbeStep.tsx",
"patterns": [
"rounded-md border border-red-300 bg-red-50",
"rounded-md border border-amber-300 bg-amber-50"
]
},
{
"path": "src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx",
"patterns": [
"border-green-300 bg-green-50",
"rounded-md border border-rose-300 bg-rose-50",
"testToneClass"
]
},
{
"path": "src/components/Settings/ConnectionEditor/CredentialSlots/TrueNASCredentialSlot.tsx",
"patterns": [
"rounded-md border border-amber-300 bg-amber-50",
"rounded-md border border-rose-300 bg-rose-50"
]
},
{
"path": "src/components/Settings/ConnectionEditor/CredentialSlots/VMwareCredentialSlot.tsx",
"patterns": [
"rounded-md border border-amber-300 bg-amber-50",
"rounded-md border border-rose-300 bg-rose-50"
]
},
{
"path": "src/components/Settings/DiagnosticsResultsPanel.tsx",
"patterns": [
@@ -2407,6 +2443,72 @@
"scripts/shared-template-audit.mjs"
]
},
{
"id": "settings-connection-editor-local-warning-callout-shell",
"category": "callout",
"summary": "Connection editor settings code must not recreate amber warning callout shells locally; compose CalloutCard compact notices so tone, spacing, and dark-mode styling stay shared.",
"canonical": {
"path": "src/components/shared/CalloutCard.tsx",
"export": "CalloutCard"
},
"scopes": ["src/components/Settings/ConnectionEditor"],
"extensions": [".tsx"],
"allPatterns": ["rounded-md border", "bg-amber-50"],
"allowedPaths": [],
"ignoredPaths": [
"src/components/Settings/ConnectionEditor/__tests__/ConnectionEditor.test.tsx",
"src/components/shared/SharedPrimitives.guardrails.test.ts"
],
"proof": [
"src/components/shared/SharedPrimitives.guardrails.test.ts",
"src/components/shared/CalloutCard.test.tsx",
"scripts/shared-template-audit.mjs"
]
},
{
"id": "settings-connection-editor-local-danger-callout-shell",
"category": "callout",
"summary": "Connection editor settings code must not recreate red or rose danger callout shells locally; compose CalloutCard compact notices so alert tone, spacing, and dark-mode styling stay shared.",
"canonical": {
"path": "src/components/shared/CalloutCard.tsx",
"export": "CalloutCard"
},
"scopes": ["src/components/Settings/ConnectionEditor"],
"extensions": [".tsx"],
"allPatterns": ["rounded-md border", "bg-rose-50"],
"allowedPaths": [],
"ignoredPaths": [
"src/components/Settings/ConnectionEditor/__tests__/ConnectionEditor.test.tsx",
"src/components/shared/SharedPrimitives.guardrails.test.ts"
],
"proof": [
"src/components/shared/SharedPrimitives.guardrails.test.ts",
"src/components/shared/CalloutCard.test.tsx",
"scripts/shared-template-audit.mjs"
]
},
{
"id": "settings-connection-editor-local-red-callout-shell",
"category": "callout",
"summary": "Connection editor settings code must not recreate red danger callout shells locally; compose CalloutCard compact notices so alert tone, spacing, and dark-mode styling stay shared.",
"canonical": {
"path": "src/components/shared/CalloutCard.tsx",
"export": "CalloutCard"
},
"scopes": ["src/components/Settings/ConnectionEditor"],
"extensions": [".tsx"],
"allPatterns": ["rounded-md border", "bg-red-50"],
"allowedPaths": [],
"ignoredPaths": [
"src/components/Settings/ConnectionEditor/__tests__/ConnectionEditor.test.tsx",
"src/components/shared/SharedPrimitives.guardrails.test.ts"
],
"proof": [
"src/components/shared/SharedPrimitives.guardrails.test.ts",
"src/components/shared/CalloutCard.test.tsx",
"scripts/shared-template-audit.mjs"
]
},
{
"id": "platform-inline-notice-local-amber-shell",
"category": "callout",
@@ -1,6 +1,7 @@
import { Component, For, Show } from 'solid-js';
import type { ProbeCandidate } from '@/api/connections';
import { Button } from '@/components/shared/Button';
import { CalloutCard } from '@/components/shared/CalloutCard';
import { formControl, formField, formHelpText, formLabel } from '@/components/shared/Form';
import { formatConnectionErrorMessage } from '@/utils/connectionErrorPresentation';
import { getInfrastructureAgentHostProfileSupportText } from '@/utils/infrastructureOnboardingPresentation';
@@ -63,41 +64,52 @@ export const AddressProbeStep: Component<AddressProbeStepProps> = (props) => {
</div>
<Show when={props.state.phase() === 'error' && props.state.errorMessage().length > 0}>
<div class="rounded-md border border-red-300 bg-red-50 px-3 py-2 text-sm text-red-800 dark:border-red-800 dark:bg-red-950/40 dark:text-red-200">
{formatConnectionErrorMessage(props.state.errorMessage()) ?? props.state.errorMessage()}
</div>
<CalloutCard
role="alert"
tone="danger"
scale="compact"
padding="sm"
description={
formatConnectionErrorMessage(props.state.errorMessage()) ?? props.state.errorMessage()
}
/>
</Show>
<Show when={props.state.phase() === 'no-match'}>
<div class="rounded-md border border-amber-300 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-100">
<div class="font-medium">No supported API-backed platform detected at that address.</div>
<div class="mt-1 text-xs">
<Show when={props.onChooseSourceTypeInstead}>
<button
type="button"
onClick={props.onChooseSourceTypeInstead}
class="font-medium underline underline-offset-2 hover:text-amber-950 dark:hover:text-amber-50"
<CalloutCard
tone="warning"
scale="compact"
padding="sm"
title="No supported API-backed platform detected at that address."
description={
<>
<Show when={props.onChooseSourceTypeInstead}>
<button
type="button"
onClick={props.onChooseSourceTypeInstead}
class="font-semibold underline underline-offset-2"
>
Choose a source type instead
</button>
<span>. </span>
</Show>
If this is one of the supported {getInfrastructureAgentHostProfileSupportText()},{' '}
<Show
when={props.onInstallAgent}
fallback={<span class="font-medium">install Pulse Agent instead</span>}
>
Choose a source type instead
</button>
<span>. </span>
</Show>
If this is one of the supported {getInfrastructureAgentHostProfileSupportText()},{' '}
<Show
when={props.onInstallAgent}
fallback={<span class="font-medium">install Pulse Agent instead</span>}
>
<button
type="button"
onClick={props.onInstallAgent}
class="font-medium underline underline-offset-2 hover:text-amber-950 dark:hover:text-amber-50"
>
install Pulse Agent instead
</button>
</Show>
.
</div>
</div>
<button
type="button"
onClick={props.onInstallAgent}
class="font-semibold underline underline-offset-2"
>
install Pulse Agent instead
</button>
</Show>
.
</>
}
/>
</Show>
<Show when={props.state.phase() === 'detected' && props.state.candidates().length > 0}>
@@ -1,5 +1,6 @@
import { Component, Show, createSignal, onMount } from 'solid-js';
import { Button } from '@/components/shared/Button';
import { CalloutCard } from '@/components/shared/CalloutCard';
import {
formCheckbox,
formControl,
@@ -103,11 +104,6 @@ const payloadFromForm = (form: AvailabilityForm): AvailabilityTarget => {
};
};
const testToneClass = (result: AvailabilityTestResponse) =>
result.success
? 'border-green-300 bg-green-50 text-green-800 dark:border-green-900 dark:bg-green-950 dark:text-green-200'
: 'border-rose-300 bg-rose-50 text-rose-800 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-200';
const presetSensitiveFormKeys: ReadonlySet<keyof AvailabilityForm> = new Set([
'path',
'port',
@@ -379,33 +375,41 @@ export const AvailabilityTargetSlot: Component<AvailabilityTargetSlotProps> = (p
<Show when={testResult()}>
{(result) => (
<div class={`rounded-md border px-4 py-3 text-sm ${testToneClass(result())}`}>
{result().success
? `Probe reached the target in ${result().latencyMillis} ms.`
: result().error || 'Probe failed.'}
</div>
<CalloutCard
role={result().success ? 'status' : 'alert'}
tone={result().success ? 'success' : 'danger'}
scale="compact"
padding="sm"
description={
result().success
? `Probe reached the target in ${result().latencyMillis} ms.`
: result().error || 'Probe failed.'
}
/>
)}
</Show>
<Show when={error()}>
{(message) => (
<div
<CalloutCard
role="alert"
class="rounded-md border border-rose-300 bg-rose-50 px-4 py-3 text-sm text-rose-800 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-200"
>
{message()}
</div>
tone="danger"
scale="compact"
padding="sm"
description={message()}
/>
)}
</Show>
<Show when={props.deleteError}>
{(message) => (
<div
<CalloutCard
role="alert"
class="rounded-md border border-rose-300 bg-rose-50 px-4 py-3 text-sm text-rose-800 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-200"
>
{message()}
</div>
tone="danger"
scale="compact"
padding="sm"
description={message()}
/>
)}
</Show>
@@ -1,5 +1,6 @@
import { Component, Show, createEffect, onMount } from 'solid-js';
import { Button } from '@/components/shared/Button';
import { CalloutCard } from '@/components/shared/CalloutCard';
import {
formCheckbox,
formControl,
@@ -58,10 +59,15 @@ export const TrueNASCredentialSlot: Component<TrueNASCredentialSlotProps> = (pro
return (
<div class="space-y-6">
<Show when={props.state.featureDisabled()}>
<div class="rounded-md border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-900 dark:border-amber-700 dark:bg-amber-950/40 dark:text-amber-200">
{props.state.featureDisabledMessage() ||
'TrueNAS connections are disabled for this Pulse tier.'}
</div>
<CalloutCard
tone="warning"
scale="compact"
padding="sm"
description={
props.state.featureDisabledMessage() ||
'TrueNAS connections are disabled for this Pulse tier.'
}
/>
</Show>
<Show when={!props.state.featureDisabled()}>
@@ -299,12 +305,13 @@ export const TrueNASCredentialSlot: Component<TrueNASCredentialSlotProps> = (pro
<Show when={props.deleteError}>
{(message) => (
<div
<CalloutCard
role="alert"
class="rounded-md border border-rose-300 bg-rose-50 px-4 py-3 text-sm text-rose-800 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-200"
>
{message()}
</div>
tone="danger"
scale="compact"
padding="sm"
description={message()}
/>
)}
</Show>
@@ -59,10 +59,15 @@ export const VMwareCredentialSlot: Component<VMwareCredentialSlotProps> = (props
return (
<div class="space-y-6">
<Show when={props.state.featureDisabled()}>
<div class="rounded-md border border-amber-300 bg-amber-50 px-4 py-3 text-sm text-amber-900 dark:border-amber-700 dark:bg-amber-950/40 dark:text-amber-200">
{props.state.featureDisabledMessage() ||
'VMware connections are disabled for this Pulse tier.'}
</div>
<CalloutCard
tone="warning"
scale="compact"
padding="sm"
description={
props.state.featureDisabledMessage() ||
'VMware connections are disabled for this Pulse tier.'
}
/>
</Show>
<Show when={!props.state.featureDisabled()}>
@@ -119,9 +124,7 @@ export const VMwareCredentialSlot: Component<VMwareCredentialSlotProps> = (props
<input
class={formControl}
value={props.state.form().username}
onInput={(event) =>
props.state.updateForm({ username: event.currentTarget.value })
}
onInput={(event) => props.state.updateForm({ username: event.currentTarget.value })}
placeholder="administrator@vsphere.local"
/>
</label>
@@ -131,9 +134,7 @@ export const VMwareCredentialSlot: Component<VMwareCredentialSlotProps> = (props
class={formControl}
type="password"
value={props.state.form().password}
onInput={(event) =>
props.state.updateForm({ password: event.currentTarget.value })
}
onInput={(event) => props.state.updateForm({ password: event.currentTarget.value })}
placeholder={
props.state.form().hasStoredPassword
? 'Saved password retained unless replaced'
@@ -169,9 +170,7 @@ export const VMwareCredentialSlot: Component<VMwareCredentialSlotProps> = (props
type="checkbox"
class={formCheckbox}
checked={props.state.form().enabled}
onChange={(event) =>
props.state.updateForm({ enabled: event.currentTarget.checked })
}
onChange={(event) => props.state.updateForm({ enabled: event.currentTarget.checked })}
/>
<span class="text-sm text-base-content">Enable this vCenter connection</span>
</label>
@@ -246,12 +245,13 @@ export const VMwareCredentialSlot: Component<VMwareCredentialSlotProps> = (props
<Show when={props.deleteError}>
{(message) => (
<div
<CalloutCard
role="alert"
class="rounded-md border border-rose-300 bg-rose-50 px-4 py-3 text-sm text-rose-800 dark:border-rose-900 dark:bg-rose-950 dark:text-rose-200"
>
{message()}
</div>
tone="danger"
scale="compact"
padding="sm"
description={message()}
/>
)}
</Show>
@@ -326,9 +326,7 @@ export const VMwareCredentialSlot: Component<VMwareCredentialSlotProps> = (props
variant="outline"
size="settingsAction"
onClick={() => void props.state.previewCurrentForm()}
disabled={
props.state.saving() || props.state.testing() || props.state.previewing()
}
disabled={props.state.saving() || props.state.testing() || props.state.previewing()}
title="Show the resources Pulse would ingest from this connection without saving or starting polling."
>
{props.state.previewing() ? 'Previewing…' : 'Preview impact'}
@@ -59,6 +59,7 @@ import nodeModalMonitoringSectionSource from '../NodeModalMonitoringSection.tsx?
import nodeModalSetupGuideSectionSource from '../NodeModalSetupGuideSection.tsx?raw';
import nodeModalStatusFooterSource from '../NodeModalStatusFooter.tsx?raw';
import nodeModalStateSource from '../useNodeModalState.ts?raw';
import availabilityTargetSlotSource from '../ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx?raw';
import trueNASCredentialSlotSource from '../ConnectionEditor/CredentialSlots/TrueNASCredentialSlot.tsx?raw';
import vmwareCredentialSlotSource from '../ConnectionEditor/CredentialSlots/VMwareCredentialSlot.tsx?raw';
import organizationAccessManagementSectionSource from '../OrganizationAccessManagementSection.tsx?raw';
@@ -321,6 +322,9 @@ describe('settings architecture guardrails', () => {
reportingPanelSource,
securityAuthPanelSource,
securityOverviewPanelSource,
addressProbeStepSource,
availabilityTargetSlotSource,
trueNASCredentialSlotSource,
vmwareCredentialSlotSource,
]) {
expect(source).toContain('CalloutCard');
@@ -329,6 +333,15 @@ describe('settings architecture guardrails', () => {
expect(discoverySettingsFormSource).toContain('scale="compact"');
expect(aiProviderConfigurationSectionSource).toContain('scale="compact"');
expect(diagnosticsResultsPanelSource).toContain('scale="compact"');
for (const source of [
addressProbeStepSource,
availabilityTargetSlotSource,
trueNASCredentialSlotSource,
vmwareCredentialSlotSource,
]) {
expect(source).toContain('scale="compact"');
expect(source).toContain('padding="sm"');
}
expect(discoverySettingsFormSource).not.toContain(
'rounded-md border border-amber-200 bg-amber-50/80',
);
@@ -336,6 +349,25 @@ describe('settings architecture guardrails', () => {
expect(diagnosticsResultsPanelSource).not.toContain(
'rounded-md border border-amber-200 bg-amber-50',
);
expect(addressProbeStepSource).not.toContain('rounded-md border border-red-300 bg-red-50');
expect(addressProbeStepSource).not.toContain('rounded-md border border-amber-300 bg-amber-50');
expect(availabilityTargetSlotSource).not.toContain('testToneClass');
expect(availabilityTargetSlotSource).not.toContain('border-green-300 bg-green-50');
expect(availabilityTargetSlotSource).not.toContain(
'rounded-md border border-rose-300 bg-rose-50',
);
expect(trueNASCredentialSlotSource).not.toContain(
'rounded-md border border-amber-300 bg-amber-50',
);
expect(trueNASCredentialSlotSource).not.toContain(
'rounded-md border border-rose-300 bg-rose-50',
);
expect(vmwareCredentialSlotSource).not.toContain(
'rounded-md border border-amber-300 bg-amber-50',
);
expect(vmwareCredentialSlotSource).not.toContain(
'rounded-md border border-rose-300 bg-rose-50',
);
});
it('keeps telemetry disclosure aligned with the security privacy contract', () => {
@@ -3256,15 +3256,42 @@ describe('shared primitive guardrails', () => {
requiredConsumers?: Array<{ path?: string }>;
forbiddenPatterns?: Array<{ path?: string; patterns?: string[] }>;
}>;
patternGuards?: Array<{
id: string;
canonical?: { path?: string; export?: string };
scopes?: string[];
allPatterns?: string[];
}>;
};
const registeredRule = registry.rules?.find(
(rule) => rule.id === 'settings-callout-card-shell',
);
const registeredWarningGuard = registry.patternGuards?.find(
(guard) => guard.id === 'settings-connection-editor-local-warning-callout-shell',
);
const registeredDangerGuard = registry.patternGuards?.find(
(guard) => guard.id === 'settings-connection-editor-local-danger-callout-shell',
);
const registeredRedGuard = registry.patternGuards?.find(
(guard) => guard.id === 'settings-connection-editor-local-red-callout-shell',
);
expect(registeredRule?.canonical?.path).toBe('src/components/shared/CalloutCard.tsx');
expect(registeredRule?.canonical?.export).toBe('CalloutCard');
for (const guard of [registeredWarningGuard, registeredDangerGuard, registeredRedGuard]) {
expect(guard?.canonical?.path).toBe('src/components/shared/CalloutCard.tsx');
expect(guard?.canonical?.export).toBe('CalloutCard');
expect(guard?.scopes).toEqual(['src/components/Settings/ConnectionEditor']);
expect(guard?.allPatterns).toContain('rounded-md border');
}
expect(registeredWarningGuard?.allPatterns).toContain('bg-amber-50');
expect(registeredDangerGuard?.allPatterns).toContain('bg-rose-50');
expect(registeredRedGuard?.allPatterns).toContain('bg-red-50');
expect(registeredRule?.requiredConsumers?.map((consumer) => consumer.path)).toEqual([
'src/components/Settings/AIProviderConfigurationSection.tsx',
'src/components/Settings/ConnectionEditor/AddressProbeStep.tsx',
'src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx',
'src/components/Settings/ConnectionEditor/CredentialSlots/TrueNASCredentialSlot.tsx',
'src/components/Settings/ConnectionEditor/CredentialSlots/VMwareCredentialSlot.tsx',
'src/components/Settings/DiagnosticsResultsPanel.tsx',
'src/components/Settings/DiscoverySettingsForm.tsx',
@@ -3279,6 +3306,35 @@ describe('shared primitive guardrails', () => {
path: 'src/components/Settings/AIProviderConfigurationSection.tsx',
patterns: expect.arrayContaining(['rounded border border-red-200']),
}),
expect.objectContaining({
path: 'src/components/Settings/ConnectionEditor/AddressProbeStep.tsx',
patterns: expect.arrayContaining([
'rounded-md border border-red-300 bg-red-50',
'rounded-md border border-amber-300 bg-amber-50',
]),
}),
expect.objectContaining({
path: 'src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx',
patterns: expect.arrayContaining([
'border-green-300 bg-green-50',
'rounded-md border border-rose-300 bg-rose-50',
'testToneClass',
]),
}),
expect.objectContaining({
path: 'src/components/Settings/ConnectionEditor/CredentialSlots/TrueNASCredentialSlot.tsx',
patterns: expect.arrayContaining([
'rounded-md border border-amber-300 bg-amber-50',
'rounded-md border border-rose-300 bg-rose-50',
]),
}),
expect.objectContaining({
path: 'src/components/Settings/ConnectionEditor/CredentialSlots/VMwareCredentialSlot.tsx',
patterns: expect.arrayContaining([
'rounded-md border border-amber-300 bg-amber-50',
'rounded-md border border-rose-300 bg-rose-50',
]),
}),
expect.objectContaining({
path: 'src/components/Settings/DiagnosticsResultsPanel.tsx',
patterns: expect.arrayContaining(['rounded-md border border-amber-200 bg-amber-50']),
@@ -3303,11 +3359,42 @@ describe('shared primitive guardrails', () => {
reportingPanelSource,
securityAuthPanelSource,
securityOverviewPanelSource,
addressProbeStepSource,
availabilityTargetSlotSource,
trueNASCredentialSlotSource,
vmwareCredentialSlotSource,
]) {
expect(source).toContain('CalloutCard');
}
for (const source of [
addressProbeStepSource,
availabilityTargetSlotSource,
trueNASCredentialSlotSource,
vmwareCredentialSlotSource,
]) {
expect(source).toContain('scale="compact"');
expect(source).toContain('padding="sm"');
}
expect(aiProviderConfigurationSectionSource).not.toContain('rounded border border-red-200');
expect(addressProbeStepSource).not.toContain('rounded-md border border-red-300 bg-red-50');
expect(addressProbeStepSource).not.toContain('rounded-md border border-amber-300 bg-amber-50');
expect(availabilityTargetSlotSource).not.toContain('testToneClass');
expect(availabilityTargetSlotSource).not.toContain('border-green-300 bg-green-50');
expect(availabilityTargetSlotSource).not.toContain(
'rounded-md border border-rose-300 bg-rose-50',
);
expect(trueNASCredentialSlotSource).not.toContain(
'rounded-md border border-amber-300 bg-amber-50',
);
expect(trueNASCredentialSlotSource).not.toContain(
'rounded-md border border-rose-300 bg-rose-50',
);
expect(vmwareCredentialSlotSource).not.toContain(
'rounded-md border border-amber-300 bg-amber-50',
);
expect(vmwareCredentialSlotSource).not.toContain(
'rounded-md border border-rose-300 bg-rose-50',
);
expect(diagnosticsResultsPanelSource).not.toContain(
'rounded-md border border-amber-200 bg-amber-50',
);
@@ -2860,7 +2860,7 @@ class SubsystemLookupTest(unittest.TestCase):
{
"heading": "## Shared Boundaries",
"path": "internal/api/access_control_handlers.go",
"line": 465,
"line": 469,
"heading_line": 123,
}
],