mirror of
https://github.com/rcourtman/Pulse.git
synced 2026-09-10 02:25:56 +00:00
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:
@@ -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"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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())}
|
||||
/>
|
||||
|
||||
+8
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user