Skip to content

Fix Resources accessibility keyboard and reflow paths#17926

Merged
adamint merged 35 commits into
microsoft:mainfrom
adamint:adamint/a11y-resources-keyboard-focus
Jul 9, 2026
Merged

Fix Resources accessibility keyboard and reflow paths#17926
adamint merged 35 commits into
microsoft:mainfrom
adamint:adamint/a11y-resources-keyboard-focus

Conversation

@adamint

@adamint adamint commented Jun 4, 2026

Copy link
Copy Markdown
Member

Description

Fixes #17466
Fixes #17656
Fixes #17651
Fixes #17654
Fixes #17469

Condenses the Resources-page accessibility fixes into one related PR. This keeps the keyboard/menu work together: View options now announces expanded/collapsed state, restores focus after item selection, nested row controls no longer also activate the parent row, and narrow Resources tab strips keep the active tab visible and keyboard-reachable.

This PR intentionally does not include the temporary Aspire-side popup Tab-navigation workaround from the earlier draft. Per review feedback, that path should be fixed in FluentUI and picked up by the dashboard through the library.

Supersedes the smaller draft PRs #17774, #17776, #17789, and #17791.

User-facing usage

Keyboard and screen reader users get predictable Resources-page navigation:

View options closed: aria-expanded="false"
View options open:   aria-expanded="true"
After menu item selection: focus returns to View options
Enter on a resource URL link: opens only the URL, not row details too
Enter on "Open in text visualizer": opens only the text visualizer, not row details too
320px Resources tabs: the selected Resources tab remains visible and keyboard-reachable
360px horizontal Resources tabs: Resources, Parameters, and Graph remain visible just above ultra-low-width reflow

Preserved scenarios and proof

Issue Scenario Proof
#17466 View options exposes reliable expanded/collapsed state. Source PR #17774. Artifact folder: https://github.com/adamint/aspire/tree/a11y-artifacts-20260601042635/17466. Visual proof: https://raw.githubusercontent.com/adamint/aspire/c39fcb45309be7a7cf64777cebc78fa0ca482095/proof/a11y/17774/pr-17774-visual-proof.mp4
#17656 Selecting Collapse child resources returns focus to the View options trigger. Source PR #17776. Artifact folder: https://github.com/adamint/aspire/tree/a11y-artifacts-20260601042635/17656. Visual proof: https://raw.githubusercontent.com/adamint/aspire/cd33e79ec249a7354240cc851b921502ada33d1a/proof/a11y/17776/pr-17776-visual-proof.mp4
#17651 Enter on a row child control opens only that child action. Source PR #17789. Artifact folder: https://github.com/adamint/aspire/tree/a11y-artifacts-20260601042635/17651. Visual proof: https://raw.githubusercontent.com/adamint/aspire/cd33e79ec249a7354240cc851b921502ada33d1a/proof/a11y/17789/pr-17789-visual-proof.mp4
#17654 The selected Resources tab stays visible within the 320px viewport, and all tabs remain visible at the narrow horizontal 360px viewport just above ultra-low-width reflow. Source PR #17791. Artifact folder: https://github.com/adamint/aspire/tree/a11y-artifacts-20260601042635/17654. Visual proof: https://raw.githubusercontent.com/adamint/aspire/cd33e79ec249a7354240cc851b921502ada33d1a/proof/a11y/17791/pr-17791-visual-proof.mp4. Final screenshot/log proof: https://github.com/adamint/aspire/tree/a4e633c765/proof/a11y/17926/final

Validation

./restore.sh

dotnet test --project tests/Aspire.Dashboard.Components.Tests/Aspire.Dashboard.Components.Tests.csproj --no-launch-profile -- --filter-class "*.AspireMenuButtonTests" --filter-class "*.AspireMenuTests" --filter-class "*.ResourcesTests" --filter-class "*.ResourceDetailsTests" --filter-class "*.ConsoleLogsTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"

dotnet test --project tests/Aspire.Dashboard.Tests/Aspire.Dashboard.Tests.csproj --no-launch-profile -- --filter-method "*.UrlLink_EnterDoesNotOpenResourceDetails" --filter-method "*.ResourceViewTabs_RemainVisibleAtNarrowViewport" --filter-method "*.ResourceViewTabs_RemainVisibleAtNarrowHorizontalViewport" --filter-not-trait "quarantined=true"

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

@github-actions

github-actions Bot commented Jun 4, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 17926

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 17926"

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR consolidates multiple Aspire Dashboard Resources page accessibility fixes, improving keyboard/screen reader behavior for menu expanded state, focus restoration, tab reflow at narrow widths, and predictable Tab/Escape navigation in popups/menus.

Changes:

  • Adds opt-in popup/menu keyboard focus navigation (Tab/Shift+Tab/Escape) via new JS handlers + a small Blazor wrapper component.
  • Fixes Resources page keyboard interaction issues (View options focus restoration, preventing nested row controls from also triggering row activation) and improves tab header reflow/visibility at narrow widths.
  • Expands automated coverage with new bUnit tests, a Node-based JS test for the Resources grid keyboard module, and a Playwright integration test for focus order.
Show a summary per file
File Description
tests/Aspire.Dashboard.Tests/Integration/Playwright/ResourcesTests.cs Adds a Playwright regression test validating Tab focus after closing the View options menu.
tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/MockDashboardClient.cs Allows injecting a custom resources list for integration scenarios.
tests/Aspire.Dashboard.Tests/Integration/Playwright/Infrastructure/DashboardServerFixture.cs Adds fixture extensibility to provide custom resource data via the mock dashboard client.
tests/Aspire.Dashboard.Components.Tests/Shared/ResourceSetupHelpers.cs Stubs the new Resources page JS module initialization/disposal for component tests.
tests/Aspire.Dashboard.Components.Tests/Shared/FluentUISetupHelpers.cs Adds global JSInterop stubs for popup keyboard navigation used by new/updated components.
tests/Aspire.Dashboard.Components.Tests/Pages/ResourcesTests.cs Adds tests for ultra-low-width tab orientation, JS module behavior, and focus restoration wiring.
tests/Aspire.Dashboard.Components.Tests/Pages/ConsoleLogsTests.cs Updates menu-item activation tests to invoke menu-item models directly (async) rather than clicking DOM elements.
tests/Aspire.Dashboard.Components.Tests/Controls/ResourceDetailsTests.cs Updates masking toggle tests to invoke Aspire menu item models directly.
tests/Aspire.Dashboard.Components.Tests/Controls/AspirePopupFocusNavigationTests.cs Adds tests for initializing/closing popup keyboard navigation.
tests/Aspire.Dashboard.Components.Tests/Controls/AspireMenuTests.cs Adds tests covering menu keyboard navigation init/dispose and focus restoration behavior.
tests/Aspire.Dashboard.Components.Tests/Controls/AspireMenuButtonTests.cs Adds a regression test ensuring aria-expanded is reliably true/false.
src/Aspire.Dashboard/wwwroot/js/app.js Introduces initializeAspirePopupKeyboardNavigation / disposeAspirePopupKeyboardNavigation and focus traversal helpers.
src/Aspire.Dashboard/Components/ResourcesGridColumns/UrlsColumnDisplay.razor Wraps overflow URL popover content in popup focus navigation to fix Tab order.
src/Aspire.Dashboard/Components/Pages/Resources.razor.js Adds a Resources-scoped JS module to stop row activation when Enter originates from interactive descendants.
src/Aspire.Dashboard/Components/Pages/Resources.razor.css Improves tab header overflow behavior so focused/selected tabs remain reachable/visible at narrow widths.
src/Aspire.Dashboard/Components/Pages/Resources.razor.cs Imports/initializes/disposes the new Resources grid keyboard activation JS module.
src/Aspire.Dashboard/Components/Pages/Resources.razor Enables focus restoration on View options; adds popup focus navigation wrapper; makes tabs vertical at ultra-low width; captures grid container element reference.
src/Aspire.Dashboard/Components/Controls/Chart/ChartFilters.razor Wraps chart filter popover content in popup focus navigation for predictable Tab behavior.
src/Aspire.Dashboard/Components/Controls/AspirePopupFocusNavigation.razor.cs New wrapper component that wires popup open/close state to JS keyboard navigation logic.
src/Aspire.Dashboard/Components/Controls/AspirePopupFocusNavigation.razor New markup wrapper providing a stable popup root element id.
src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor.cs Adds RestoreFocusOnItemClick parameter (documented) to enable opt-in focus restoration.
src/Aspire.Dashboard/Components/Controls/AspireMenuButton.razor Ensures aria-expanded is always "true"/"false" and forwards focus-restore option to AspireMenu.
src/Aspire.Dashboard/Components/Controls/AspireMenu.razor.cs Adds JS-driven popup keyboard navigation and opt-in focus restoration after item click; manages disposal.
src/Aspire.Dashboard/Components/Controls/AspireMenu.razor Assigns an id to the underlying FluentMenu to support JS focus navigation targeting.

Copilot's findings

  • Files reviewed: 24/24 changed files
  • Comments generated: 4

Comment thread src/Aspire.Dashboard/Components/Pages/Resources.razor.js Outdated
Comment thread tests/Aspire.Dashboard.Components.Tests/Controls/AspireMenuTests.cs Outdated
Comment thread tests/Aspire.Dashboard.Components.Tests/Controls/AspireMenuTests.cs Outdated

@adamint adamint left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the Resources accessibility changes with multiple passes and ran the targeted dashboard component validation. I did not find any blocking issues.

Comment thread src/Aspire.Dashboard/Components/Controls/AspireMenu.razor.cs Outdated
Comment thread src/Aspire.Dashboard/Components/Controls/AspirePopupFocusNavigation.razor.cs Outdated
Comment thread src/Aspire.Dashboard/Components/Pages/Resources.razor.js Outdated
@adamint
adamint marked this pull request as draft June 11, 2026 19:55
Copilot AI review requested due to automatic review settings June 11, 2026 20:01
@adamint
adamint marked this pull request as ready for review June 11, 2026 20:02
@adamint
adamint requested a review from JamesNK June 11, 2026 20:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 24/24 changed files
  • Comments generated: 0 new

@adamint
adamint marked this pull request as draft June 12, 2026 16:15
@adamint
adamint marked this pull request as ready for review June 12, 2026 17:45
Copilot AI review requested due to automatic review settings June 12, 2026 17:45

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 24/24 changed files
  • Comments generated: 0 new

@adamint
adamint marked this pull request as draft June 18, 2026 02:27
Copilot AI review requested due to automatic review settings June 18, 2026 02:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot's findings

  • Files reviewed: 24/24 changed files
  • Comments generated: 0 new

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 18/19 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

Copilot AI review requested due to automatic review settings July 9, 2026 02:46
@adamint
adamint enabled auto-merge (squash) July 9, 2026 02:48

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 18/19 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment thread extension/loc/xlf/aspire-vscode.xlf Outdated
Copilot AI review requested due to automatic review settings July 9, 2026 02:55

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 18/19 changed files
  • Comments generated: 1
  • Review effort level: Medium

Comment thread extension/loc/xlf/aspire-vscode.xlf Outdated
Copilot AI review requested due to automatic review settings July 9, 2026 03:11

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 18/18 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

Copilot AI review requested due to automatic review settings July 9, 2026 03:20

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review details

  • Files reviewed: 18/18 changed files
  • Comments generated: 0 new
  • Review effort level: Medium

@adamint
adamint merged commit a14d485 into microsoft:main Jul 9, 2026
334 checks passed
@github-actions github-actions Bot added this to the 13.5 milestone Jul 9, 2026
adamint pushed a commit to adamint/aspire that referenced this pull request Jul 22, 2026
Resolves merge conflicts introduced by upstream's Resources
accessibility fixes (microsoft#17926), which landed after this PR's
keyboard-accessible tooltip work:

- AspireMenuButton.razor: kept this PR's AspireTooltip wrapper around
  the menu button while wiring through upstream's new
  RestoreFocusOnItemClick parameter on the nested <AspireMenu>.
- tests/.../Playwright/ResourcesTests.cs: this file was added
  independently on both branches (add/add conflict). Combined this
  PR's ResourceToolbarTooltip_ShowsOnKeyboardFocusAndHidesOnEscape test
  with upstream's four new tests (ViewOptionsMenu_ReportsExpandedState,
  UrlLink_EnterDoesNotOpenResourceDetails,
  ResourceViewTabs_RemainVisibleAtNarrowViewport,
  ResourceViewTabs_RemainVisibleAtNarrowHorizontalViewport), keeping
  the shared ResourcesDashboardServerFixture and
  AssertTabVisibleWithinViewportAsync helper from upstream since the
  filter button under test is independent of loaded resources.

GridValue.razor auto-merged cleanly (upstream added
@onkeydown:stopPropagation, this PR added the per-button tooltip
wiring, in non-overlapping regions). AspireTooltip.razor(.js),
ResourceActions.razor(.cs), ExceptionDetails.razor, DetailView.razor
and ResourceDetails.razor were untouched by upstream/main since the
merge base, so this PR's independent focus/hover tooltip state and the
CSS.escape anchor selector are unchanged.

Validation:
- ./restore.sh
- dotnet build src/Aspire.Dashboard/Aspire.Dashboard.csproj: 0 errors
- dotnet test tests/Aspire.Dashboard.Components.Tests (full suite): 193/193 passed
- dotnet test tests/Aspire.Dashboard.Tests -- --filter-class "*.ResourcesTests" (live Chromium): 5/5 Playwright tests passed, including ResourceToolbarTooltip_ShowsOnKeyboardFocusAndHidesOnEscape
- git diff --check: clean, no conflict markers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c0833666-e36b-4435-a085-97eb64deb98f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment