diff --git a/docs/release-control/v6/internal/subsystems/alerts.md b/docs/release-control/v6/internal/subsystems/alerts.md index d5f84143cd..cf47b6f445 100644 --- a/docs/release-control/v6/internal/subsystems/alerts.md +++ b/docs/release-control/v6/internal/subsystems/alerts.md @@ -2928,7 +2928,8 @@ hours colour the dashboard and never alert. A group switched off as a whole shows its Disabled badge and no defaults line. The CPU averaging line counts every configured profile override, including ones only the API sets. CPU averaging is tuning, not the page's answer. It is one collapsed disclosure stating the effective window and the -number of platform overrides, with the same per-profile selects inside. Docker +number of platform overrides, with the same per-profile selects inside, each +a compact shared `FormSelect` with its profile name as a visible label. Docker and Machines list their resource groups before Docker-only rules and the SMART card. Threshold values, inheritance, metric evaluation windows and persistence are unchanged. diff --git a/docs/release-control/v6/internal/subsystems/api-contracts.md b/docs/release-control/v6/internal/subsystems/api-contracts.md index 12452c53b1..d2d49ec6b7 100644 --- a/docs/release-control/v6/internal/subsystems/api-contracts.md +++ b/docs/release-control/v6/internal/subsystems/api-contracts.md @@ -1822,6 +1822,10 @@ payload shape change when the portal presents compact client rows. arbitrary remote content. Contract tests must pin redaction, unchanged- secret preservation, bounded evaluation, and the reachable-but-incorrect application case. + The settings client edits the request body in the shared `FormTextarea`, + whose size note (`Up to 8 KiB`) is the field's accessible description; + removing a stored body stays an explicit action beside it, and leaving the + field blank keeps the stored body as before. Availability target protocol vocabulary is canonicalized at the API/config boundary: clients may submit `ping` as a user-facing alias for the ICMP check, but saved targets, API reads, probe status, connections rows, and diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 38a433deaf..9bfb47ec5c 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -2464,6 +2464,12 @@ not a replacement status card, CTA band, or page-local nested card. apply controlled `value` props after options are mounted so settings panels such as Discovery show the persisted option instead of falling back to the first option while the collapsed summary shows a different value. + Its default select chrome (`formSelect` / `formSelectCompact` in + `Form.ts`) sets `appearance-none` to match the text inputs, so it also + carries the `form-select-arrow` utility, which draws the dropdown caret in + the reserved right padding (and hands the native arrow back in forced-colors + mode); without it every default `FormSelect` read as a text field. Callers + that pass their own `selectBaseClass` keep their own arrow. The Assistant runtime controls in `frontend-modern/src/components/Settings/AIRuntimeControlsSection.tsx` — e.g. the service context scan `Toggle` — are settings-shell chrome bound to the canonical diff --git a/docs/release-control/v6/internal/subsystems/unified-resources.md b/docs/release-control/v6/internal/subsystems/unified-resources.md index 7269935c42..21b335013f 100644 --- a/docs/release-control/v6/internal/subsystems/unified-resources.md +++ b/docs/release-control/v6/internal/subsystems/unified-resources.md @@ -2907,7 +2907,9 @@ Discovery-led availability proposals present their evidence and canonical attachment boundary as short, complete sentences in both the inline card and machine-wide review. The colocated component test pins that operator-facing copy so the shared copy-style audit cannot regress it back to semicolon-joined -instructions. +instructions. The card's editable check name, interval and observation +location use the shared form label and control chrome (`FormSelect` for the +two choices), so they match the availability settings form they hand off to. ### Provider workflows hydrate only their active route inventory diff --git a/frontend-modern/browser-verification/07daa1c32e41a709.json b/frontend-modern/browser-verification/07daa1c32e41a709.json new file mode 100644 index 0000000000..2d108625f2 --- /dev/null +++ b/frontend-modern/browser-verification/07daa1c32e41a709.json @@ -0,0 +1,48 @@ +{ + "version": 1, + "base_sha": "979466013876ce54a67494b0c4cda9b404ea45e8", + "verified_at": "2026-10-05T22:01:46Z", + "result": "passed", + "changed_paths": [ + "frontend-modern/src/components/Discovery/AvailabilityProposalCard.tsx", + "frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx", + "frontend-modern/src/components/shared/Form.ts", + "frontend-modern/src/features/alerts/tabs/ThresholdsTab.tsx", + "frontend-modern/src/index.css" + ], + "content_sha256": { + "frontend-modern/src/components/Discovery/AvailabilityProposalCard.tsx": "bfa50b13f521b9a91b57495eec3a827a71a413a0eed0832be0d58444e8ca3ee7", + "frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx": "af7a1d322045c33747600466a580ae6148f752c8d847404a6f5b04fa49fd53e7", + "frontend-modern/src/components/shared/Form.ts": "e47d06ebbbba68ac0d19769acac83a0ca930b1e2d5a7258c7c1fcf43e82ce44a", + "frontend-modern/src/features/alerts/tabs/ThresholdsTab.tsx": "8142c12c2e6930a505e1f063aa8a396bfa45c6c55a8ea7c7d1085198a9f3915f", + "frontend-modern/src/index.css": "b8ae1a40f0915ff034e51a16a94e0c2159ac726ef0afdfdd8fb321f5d5f7dc5e" + }, + "routes": [ + "/alerts/thresholds", + "/settings/monitoring/availability", + "/proxmox/overview" + ], + "viewports": [ + { + "width": 1440, + "height": 1100 + }, + { + "width": 390, + "height": 1100 + } + ], + "states": [ + "Alerts > Thresholds > CPU averaging opened: all 10 per-platform selects render through FormSelect with their visible label tied by for/id, values intact (5-minute average, Inherit), 32px desktop / 44px phone, and a dropdown caret; main showed native selects", + "Settings > Availability checks > Add service/device check with an HTTP POST probe: Request body (optional) is a FormTextarea with its label tied and the 8 KiB help linked by aria-describedby, 96px tall on desktop and phone (main: 54px on desktop because formControl's sm:min-h-9 overrode min-h-24; no help link)", + "Existing default FormSelects in that dialog (Preset, Target type, Probe) now show a caret; on main they had appearance-none and no arrow, reading as text fields", + "Proxmox guest drawer Discovery tab with discovery_enabled and an availability proposal served by route interception: Check name input and the Interval / Observation location FormSelects share the shared form label and control chrome at matched heights, with carets", + "Dark mode at 1440: caret drawn in the muted text colour and readable; forced-colors emulation: select falls back to appearance auto with no background image", + "Shared template audit: only the three findings in BackupTransferDialogs.tsx and AlertDeadManDestinationSection.tsx remain (held by another session's open bg-base change)", + "Re-checked after rebasing onto 9794660138 (#2535, shared toggle): CPU averaging selects unchanged, labelled, with the caret" + ], + "interactions": [ + "Opened the CPU averaging disclosure, the availability check dialog (HTTP, POST) and a guest Discovery tab, on main (reverse-applied patch) and on the change", + "Emulated forced-colors: active" + ] +} diff --git a/frontend-modern/scripts/shared-template-registry.json b/frontend-modern/scripts/shared-template-registry.json index 04ad2b7184..68cc8cdb44 100644 --- a/frontend-modern/scripts/shared-template-registry.json +++ b/frontend-modern/scripts/shared-template-registry.json @@ -1709,7 +1709,6 @@ { "path": "src/components/shared/FilterToolbar.tsx" }, { "path": "src/features/alerts/AlertAppriseDestinationsSection.tsx" }, { "path": "src/features/alerts/AlertDeliveryRoutingSection.tsx" }, - { "path": "src/features/alerts/AlertEscalationSection.tsx" }, { "path": "src/features/alerts/AlertQuietHoursSection.tsx" }, { "path": "src/features/patrol/PatrolAttentionWorkbench.tsx" } ], @@ -6664,12 +6663,11 @@ "src/features/vmware" ], "extensions": [".tsx"], + "pathExcludes": [".test.", "__tests__"], "triggerPatterns": ["data-inline", "TableRow"], "requiredPatterns": ["InlineDetailTableRow"], "allowedPaths": [], - "ignoredPaths": [ - "src/components/Workloads/__tests__/WorkloadsSurface.performance.contract.test.tsx" - ], + "ignoredPaths": [], "proof": [ "src/components/shared/SharedPrimitives.guardrails.test.ts", "scripts/shared-template-audit.mjs" diff --git a/frontend-modern/src/__tests__/App.architecture.test.ts b/frontend-modern/src/__tests__/App.architecture.test.ts index e9d9cc865b..e02a726334 100644 --- a/frontend-modern/src/__tests__/App.architecture.test.ts +++ b/frontend-modern/src/__tests__/App.architecture.test.ts @@ -304,6 +304,18 @@ describe('App architecture', () => { ); }); + it('draws the shared select arrow that appearance-none removes', () => { + // FormSelect's default chrome sets appearance-none to match text inputs; + // without this utility every default select read as a text field. + expect(appStylesSource).toMatch( + /@utility form-select-arrow \{\s*background-image:\s*linear-gradient\(45deg, transparent 50%, var\(--color-text-muted\) 50%\)/, + ); + // Forced-colors mode drops background images, so the native arrow returns. + expect(appStylesSource).toMatch( + /@media \(forced-colors: active\) \{\s*appearance: auto !important;\s*background-image: none;/, + ); + }); + it('keeps compact phone settings spacing scoped to the settings content shell', () => { expect(appStylesSource).toContain('@media (max-width: 39.999rem)'); expect(appStylesSource).toContain( diff --git a/frontend-modern/src/components/Discovery/AvailabilityProposalCard.tsx b/frontend-modern/src/components/Discovery/AvailabilityProposalCard.tsx index 74c325d42a..4f5d36c575 100644 --- a/frontend-modern/src/components/Discovery/AvailabilityProposalCard.tsx +++ b/frontend-modern/src/components/Discovery/AvailabilityProposalCard.tsx @@ -22,6 +22,8 @@ import { } from '@/api/discovery'; import { ActionIconButton, Button } from '@/components/shared/Button'; import { Dialog } from '@/components/shared/Dialog'; +import { formControl, formField, formLabel } from '@/components/shared/Form'; +import { FormSelect } from '@/components/shared/FormSelect'; import { LoadingSpinner } from '@/components/shared/LoadingSpinner'; import type { DiscoverySummary, ResourceDiscovery, ResourceType } from '@/types/discovery'; import { @@ -298,47 +300,35 @@ export const AvailabilityProposalCard: Component

-
diff --git a/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx b/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx index 004562de2b..09292e4ce8 100644 --- a/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx +++ b/frontend-modern/src/components/Settings/ConnectionEditor/CredentialSlots/AvailabilityTargetSlot.tsx @@ -10,6 +10,7 @@ import { formLabel, } from '@/components/shared/Form'; import { FormSelect } from '@/components/shared/FormSelect'; +import { FormTextarea } from '@/components/shared/FormTextarea'; import { AvailabilityTargetsAPI, type AvailabilityProbeProtocol, @@ -776,10 +777,11 @@ export const AvailabilityTargetSlot: Component = (p -