diff --git a/docs/release-control/v6/internal/subsystems/frontend-primitives.md b/docs/release-control/v6/internal/subsystems/frontend-primitives.md index 84cb9303e..42dd72937 100644 --- a/docs/release-control/v6/internal/subsystems/frontend-primitives.md +++ b/docs/release-control/v6/internal/subsystems/frontend-primitives.md @@ -1167,6 +1167,14 @@ not a replacement status card, CTA band, or page-local nested card. disclosure; a non-expandable identity cell must otherwise expose its complete value without requiring hover. Default data rows stay single-line so scan density and row rhythm are not traded for automatic text wrapping. + When a platform table row itself owns that keyboard- and touch-operable + disclosure, `SummaryRowActionButton` removes the redundant visible chevron + below the shared mobile breakpoint while preserving the button for screen + readers and revealing it on keyboard focus. `PlatformResourceDetailToggleButton` + applies that policy to provider-native platform tables, and workload rows + opt in only while their compact row remains the disclosure target. Rows + whose click action does something other than expand details must keep a + visible disclosure control. They must not rely on a global rule that reveals every hidden column: phone priority remains source-specific because state, capacity, freshness, and recovery posture do not carry equal value for every resource diff --git a/frontend-modern/browser-verification.json b/frontend-modern/browser-verification.json index c8f355f11..1205345ac 100644 --- a/frontend-modern/browser-verification.json +++ b/frontend-modern/browser-verification.json @@ -1,15 +1,19 @@ { "version": 1, - "base_sha": "791e29ad8d4cc991837ac43d0bf7c7a42d5fa54a", - "verified_at": "2026-08-24T09:50:53Z", + "base_sha": "d50f5c664e74e3a4d6a00abf76ef1e2c554e79a6", + "verified_at": "2026-08-24T10:04:07Z", "result": "passed", - "changed_paths": ["frontend-modern/src/components/Workloads/GuestRow.tsx"], - "content_sha256": { - "frontend-modern/src/components/Workloads/GuestRow.tsx": "64776e4a32d5361a2ec541344178e9e7d1e401a3d09086e68ad4f923240bf715" - }, - "routes": [ - "/proxmox" + "changed_paths": [ + "frontend-modern/src/components/Workloads/GuestRow.tsx", + "frontend-modern/src/components/shared/SummaryRowActionButton.tsx", + "frontend-modern/src/features/platformPage/PlatformResourceDetailTableRow.tsx" ], + "content_sha256": { + "frontend-modern/src/components/Workloads/GuestRow.tsx": "f8927883b484789cae4ca999845a77a7409aed800656d931be621a1f4d996ca8", + "frontend-modern/src/components/shared/SummaryRowActionButton.tsx": "626741b5b725d19dd594a4c071888170d74b0e8352513cc2aa8557c86c986c88", + "frontend-modern/src/features/platformPage/PlatformResourceDetailTableRow.tsx": "7907a58eeace6c2648f1e92e63a27bfd82fe305347e0100b76b59ab0e367ce61" + }, + "routes": ["/proxmox"], "viewports": [ { "width": 390, @@ -21,16 +25,16 @@ } ], "states": [ - "managed hot-dev mock estate with 50 nodes and 929 guests", - "phone Workloads table filtered to one guest with the compact header and collapsed row visible", - "phone Workloads table with the guest detail row expanded", - "desktop Workloads table with the same filtered guest" + "managed hot-dev mock estate with the canonical Proxmox Nodes and Guests tables visible", + "phone Nodes and Guests tables with their collapsed rows visible", + "phone Nodes and Guests tables after row-owned detail expansion", + "desktop Nodes and Guests tables after the responsive breakpoint transition" ], "interactions": [ - "confirmed the visible disclosure control is removed from compact rows while its screen-reader and keyboard-focus path remains available", - "confirmed the Type sort header renders without clipping at 390x844 and the availability column receives a larger compact-layout share when present", - "tapped the workload name to expand the filtered phone row and confirmed the accessible disclosure changes to Collapse", - "confirmed the expanded state survives the transition from 390x844 to 1440x900 and the desktop chevron remains visible", - "restored the browser viewport and real-node runtime after verification" + "confirmed provider node and workload disclosure controls remain in the accessibility tree while their redundant phone chevrons are visually removed", + "tapped the Analytics A node row and the artifact-cache-310 guest row and confirmed both detail regions expanded", + "confirmed both disclosure controls become visible again at the 1440x900 desktop breakpoint", + "confirmed Type, ID, CPU, Mem, Disk, and Age remain separated in the compact Workloads header", + "restored the browser viewport after verification" ] } diff --git a/frontend-modern/src/components/Workloads/GuestRow.tsx b/frontend-modern/src/components/Workloads/GuestRow.tsx index 318a4babb..3cbe39a08 100644 --- a/frontend-modern/src/components/Workloads/GuestRow.tsx +++ b/frontend-modern/src/components/Workloads/GuestRow.tsx @@ -221,7 +221,7 @@ export function GuestRow(props: GuestRowProps) { subjectLabel={props.guest.name} expanded={props.isExpanded === true} controlsId={detailControlsId()} - class={usesCompactTableLayout() ? 'sr-only focus:not-sr-only' : undefined} + hideWhenRowTappableOnMobile={usesCompactTableLayout()} onAction={() => props.onClick?.()} onPreviewClear={() => props.onHoverChange?.(null)} /> diff --git a/frontend-modern/src/components/Workloads/__tests__/GuestRow.test.tsx b/frontend-modern/src/components/Workloads/__tests__/GuestRow.test.tsx index c49de4cd2..b15d2ec28 100644 --- a/frontend-modern/src/components/Workloads/__tests__/GuestRow.test.tsx +++ b/frontend-modern/src/components/Workloads/__tests__/GuestRow.test.tsx @@ -924,6 +924,7 @@ describe('GuestRow', () => { expect(disclosureButton.className).toContain('sr-only'); expect(disclosureButton.className).toContain('focus:not-sr-only'); + expect(disclosureButton.className).toContain('sm:not-sr-only'); }); it('keeps the visible disclosure control in desktop layouts', () => { diff --git a/frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts b/frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts index 7c3987ae0..c78aa720a 100644 --- a/frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts +++ b/frontend-modern/src/components/shared/SharedPrimitives.guardrails.test.ts @@ -1554,8 +1554,11 @@ describe('shared primitive guardrails', () => { expect(summaryRowActionButtonSource).toContain('aria-expanded'); expect(summaryRowActionButtonSource).toContain('aria-pressed'); expect(summaryRowActionButtonSource).toContain('data-row-action="true"'); + expect(summaryRowActionButtonSource).toContain('hideWhenRowTappableOnMobile'); + expect(summaryRowActionButtonSource).toContain('sm:not-sr-only'); expect(platformResourceDetailTableRowSource).toContain('PlatformResourceDetailToggleButton'); expect(platformResourceDetailTableRowSource).toContain('SummaryRowActionButton'); + expect(platformResourceDetailTableRowSource).toContain('hideWhenRowTappableOnMobile'); for (const source of [ guestRowSource, diff --git a/frontend-modern/src/components/shared/SummaryRowActionButton.tsx b/frontend-modern/src/components/shared/SummaryRowActionButton.tsx index b83d0ae26..209f88742 100644 --- a/frontend-modern/src/components/shared/SummaryRowActionButton.tsx +++ b/frontend-modern/src/components/shared/SummaryRowActionButton.tsx @@ -11,6 +11,7 @@ type SummaryRowActionButtonProps = subjectLabel: string; controlsId?: string; class?: string; + hideWhenRowTappableOnMobile?: boolean; onAction: () => void; onPreviewClear?: () => void; } @@ -46,12 +47,16 @@ export const SummaryRowActionButton: Component = (p scopeProps()?.pressed ? `Unpin summary scope for ${props.subjectLabel}` : `Pin summary scope for ${props.subjectLabel}`; + const mobileRowTargetClass = () => + disclosureProps()?.hideWhenRowTappableOnMobile + ? 'sr-only focus:not-sr-only sm:not-sr-only' + : ''; return (