Merge reviewed shell and settings accessibility fixes

Integrate the exact browser-verified candidate after the captured runtime changes; the histories touch disjoint paths.

Change-source: pulse-maintainer
This commit is contained in:
pulse-triage[bot]
2026-09-02 04:04:36 +01:00
6 changed files with 60 additions and 44 deletions
+23 -41
View File
@@ -1,42 +1,23 @@
{
"version": 1,
"base_sha": "2f8a4ec629b75c7ecefd38a9abc4b1511bc9a891",
"verified_at": "2026-09-01T19:54:19Z",
"base_sha": "44e274e5b386e5e990e9bf09b85b6258043487ba",
"verified_at": "2026-09-02T02:59:02Z",
"result": "passed",
"changed_paths": [
"frontend-modern/src/App.tsx",
"frontend-modern/src/AppLayout.tsx",
"frontend-modern/src/components/shared/MobileNavBar.tsx",
"frontend-modern/src/components/shared/mobileNavBarModel.ts",
"frontend-modern/src/features/home/HomePageSurface.tsx",
"frontend-modern/src/features/home/homePageModel.ts",
"frontend-modern/src/i18n/messages.de.ts",
"frontend-modern/src/i18n/messages.es.ts",
"frontend-modern/src/i18n/messages.ts",
"frontend-modern/src/routing/navigation.ts",
"frontend-modern/src/routing/resourceLinks.ts",
"frontend-modern/src/routing/routePreload.ts",
"frontend-modern/src/utils/assistantPageContext.ts"
"frontend-modern/src/components/Settings/GeneralSettingsPanel.tsx"
],
"content_sha256": {
"frontend-modern/src/App.tsx": "d5473f7838148edeaaf5564f01c166d97b6086232dcba70f6296b5c58e563a4b",
"frontend-modern/src/AppLayout.tsx": "db685903fef2a1509edd812acb3904f7ff3ffab0bbd1af2bff183c1b3ae8e373",
"frontend-modern/src/components/shared/MobileNavBar.tsx": "27358f3e77267cc111ce97b32d715b0069e2d1370ddd2b3ecc4f2d34a3e6dac5",
"frontend-modern/src/components/shared/mobileNavBarModel.ts": "ab9e69d379579d02e33a2abd1224d8c13e6543dd6aa0ad40cc1f65697a289d04",
"frontend-modern/src/features/home/HomePageSurface.tsx": "deleted",
"frontend-modern/src/features/home/homePageModel.ts": "deleted",
"frontend-modern/src/i18n/messages.de.ts": "602246d3d4ce11a1a8a914027d950a30ba1f2f25850c385836013853957c2d0c",
"frontend-modern/src/i18n/messages.es.ts": "ed5a749603efad29293cd6964215f6edc21a4ce14bca99a48429ddd120770263",
"frontend-modern/src/i18n/messages.ts": "43a757e00eaa7879c400895c9c59a930e31513dbf03e0ae6d60f7e72a6ad9962",
"frontend-modern/src/routing/navigation.ts": "8ae1ad012e60ef345ffb3d18bb66f5d8af056758109655a8ec362ebf4c7b9556",
"frontend-modern/src/routing/resourceLinks.ts": "dee9a426de785e23390ba49c9067f55c8f923cecfce1247ff18f9b1004e0cc90",
"frontend-modern/src/routing/routePreload.ts": "ee79d423db0afcf8d76d1d39da59a13cb2c98e908e516a016cbb425982b25873",
"frontend-modern/src/utils/assistantPageContext.ts": "bfca19b4e183777ee1535073d48cce92ce31980f4105bab337f33a28538a6a35"
"frontend-modern/src/AppLayout.tsx": "be386bcaa58656a9397fed71a9d70147e2540ff6cb4aff53261d9c03e6ca3834",
"frontend-modern/src/components/Settings/GeneralSettingsPanel.tsx": "f1e1c0630cc841ae2820b15130a887aeef0ec7838cb93f652213ebb8fb278199"
},
"routes": [
"/",
"/home",
"/proxmox/overview"
"/actions",
"/alerts/overview",
"/settings/infrastructure",
"/settings/system-general",
"/patrol",
"/"
],
"viewports": [
{
@@ -44,21 +25,22 @@
"height": 720
},
{
"width": 375,
"height": 812
"width": 390,
"height": 844
}
],
"states": [
"signed-in desktop shell with primary tabs Proxmox, Docker, Kubernetes, TrueNAS, vSphere, Machines and no Home tab",
"/home renders the Page Not Found surface with 'No route matched /home' and a Go to workspace button",
"signed-in narrow shell on /proxmox/overview with the bottom navigation bar showing Proxmox, Alerts, Patrol, Actions, More and no Home entry",
"More navigation sheet open at narrow width listing Settings only",
"no horizontal page overflow at 375px; no console errors at either width"
"authenticated empty Actions surface at phone width with the RC Preview badge visible, no horizontal overflow, and the skip link focused",
"authenticated General Settings surface with Full-width mode off and Outbound usage telemetry on, each exposed as a named pressed-state button",
"authenticated Alerts, Infrastructure, General Settings, and Patrol surfaces with reduced motion enabled and no automatically detectable WCAG A/AA violations",
"Add infrastructure dialog open with its accessible description, close control focused, and underlying shell retained",
"logged-out welcome surface with reduced motion enabled and no automatically detectable WCAG A/AA violations"
],
"interactions": [
"navigated to / and /home at desktop width and read the rendered nav and main headings",
"pressed Go to workspace on the /home not-found page and landed on /proxmox/overview",
"resized to 375x812, loaded /proxmox/overview, opened the More navigation sheet, closed it with Escape",
"confirmed the default landing route and nav order are unchanged from the parent revision apart from the removed Home entry"
"tabbed from the document start to Skip to main content at phone width, activated it with Enter, and confirmed focus moved to the main landmark",
"inspected the rendered 390x844 Actions screenshot for badge contrast, placement, clipping, scrolling, and bottom-navigation coherence",
"navigated the authenticated desktop routes and scanned their rendered states for WCAG A/AA violations and unexpected reduced-motion effects",
"opened Add infrastructure, verified initial close-control focus and dialog description, dismissed it with Escape, and confirmed focus returned to Add infrastructure",
"opened the logged-out entry surface and verified its heading, disabled welcome/form motion, and accessibility scan"
]
}
+5 -1
View File
@@ -258,6 +258,7 @@ export function AppLayout(props: AppLayoutProps) {
const [skipLinkFocused, setSkipLinkFocused] = createSignal(false);
const [primaryRouteMemoryVersion, setPrimaryRouteMemoryVersion] = createSignal(0);
let headerEl: HTMLDivElement | undefined;
let mainContentEl: HTMLElement | undefined;
let assistantLauncherEl: HTMLButtonElement | undefined;
let restoreAssistantLauncherFocus = false;
let headerHideTimeout: ReturnType<typeof setTimeout> | undefined;
@@ -730,6 +731,7 @@ export function AppLayout(props: AppLayoutProps) {
jump past the chrome straight into the page content. */}
<a
href="#main"
onClick={() => mainContentEl?.focus()}
onFocus={() => setSkipLinkFocused(true)}
onBlur={() => setSkipLinkFocused(false)}
class={
@@ -814,7 +816,7 @@ export function AppLayout(props: AppLayoutProps) {
</span>
</Show>
<Show when={props.versionInfo()?.channel === 'rc'}>
<span class="text-xs px-1.5 py-0.5 bg-orange-500 text-white rounded font-bold">
<span class="text-xs px-1.5 py-0.5 bg-orange-700 text-white rounded font-bold">
Preview
</span>
</Show>
@@ -980,7 +982,9 @@ export function AppLayout(props: AppLayoutProps) {
</Show>
<main
ref={mainContentEl}
id="main"
tabindex="-1"
class="tab-content mb-1 block rounded-b rounded-tl rounded-tr bg-surface shadow sm:mb-2"
>
<div class="pulse-panel">
@@ -174,7 +174,16 @@ describe('AppLayout navigation icons', () => {
expect(container.querySelector('.pulse-shell')).toHaveClass('pb-safe-or-14');
expect(container.querySelector('.pulse-shell')).not.toHaveClass('pb-safe-or-16');
expect(container.querySelector('.header')).toHaveClass('mb-1', 'sm:mb-3');
expect(container.querySelector('main')).toHaveClass('mb-1', 'sm:mb-2');
const main = container.querySelector('main');
expect(main).toHaveClass('mb-1', 'sm:mb-2');
expect(main).toHaveAttribute('id', 'main');
expect(main).toHaveAttribute('tabindex', '-1');
const skipLink = screen.getByRole('link', { name: 'Skip to main content' });
expect(skipLink).toHaveAttribute('href', '#main');
fireEvent.click(skipLink);
expect(main).toHaveFocus();
expect(screen.getByText('Preview')).toHaveClass('bg-orange-700', 'text-white');
expect(screen.getByText('Preview')).not.toHaveClass('bg-orange-500');
expect(container.querySelector('footer')).toHaveClass('pulse-footer', 'px-2', 'sm:px-4');
const desktopNav = screen.getByRole('navigation', { name: 'Primary navigation' });
@@ -223,6 +223,7 @@ export const GeneralSettingsPanel: Component<GeneralSettingsPanelProps> = (props
<Toggle
checked={layoutStore.isFullWidth()}
class="shrink-0"
ariaLabel={t('settings.general.fullWidth.title')}
onChange={() => layoutStore.toggle()}
/>
</div>
@@ -276,6 +277,7 @@ export const GeneralSettingsPanel: Component<GeneralSettingsPanelProps> = (props
<Toggle
checked={props.telemetryEnabled()}
class="shrink-0"
ariaLabel={t('settings.general.telemetry.title')}
disabled={props.telemetryEnabledLocked() || props.savingTelemetry()}
onChange={() => props.handleTelemetryEnabledChange(!props.telemetryEnabled())}
/>
@@ -86,6 +86,14 @@ describe('GeneralSettingsPanel localization', () => {
).toBeInTheDocument();
expect(screen.getByText('Usage data and privacy')).toBeInTheDocument();
expect(screen.getByText('Outbound usage telemetry')).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Full-width mode' })).toHaveAttribute(
'aria-pressed',
'false',
);
expect(screen.getByRole('button', { name: 'Outbound usage telemetry' })).toHaveAttribute(
'aria-pressed',
'true',
);
expect(screen.getByRole('button', { name: 'Preview payload' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: 'Reset ID' })).toBeInTheDocument();
expect(screen.getByText('Monitoring cadence')).toBeInTheDocument();
@@ -137,8 +137,19 @@ test("Actions remains named, directly reachable, keyboard accessible, and free o
document.documentElement.clientWidth,
);
expect(overflow).toBeFalsy();
const skipLink = page.getByRole("link", { name: "Skip to main content" });
await page.evaluate(() => {
document.body.tabIndex = -1;
document.body.focus();
document.body.removeAttribute("tabindex");
});
await page.keyboard.press("Tab");
await expect(page.locator(":focus")).toBeVisible();
await expect(skipLink).toBeFocused();
await expect(skipLink).toBeVisible();
await page.keyboard.press("Enter");
await expect(page.locator("#main")).toBeFocused();
await testInfo.attach("actions-phone-width", {
body: await page.screenshot(),
contentType: "image/png",