Repository navigation
Keep dashboard resource filters within the viewport - #21692
Sébastien Ros (sebastienros) merged 6 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 21692Or
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 21692" |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
🟡 Changes recommended
The resize test can time out or pass against stale desktop markup because it does not wait for the responsive mode switch and open the drawer before checking the filter trigger and bounds.
1 open issue
What changed in this PR
Keeps the dashboard resource Filters popover within the viewport while preserving existing filter behavior and controls.
Changes:
- Bounds the popover using CSS anchor positioning and available viewport space.
- Makes long filter lists scroll independently while keeping the header and footer from shrinking.
- Adds browser coverage for containment, filter actions, dismissal, and responsive resizing.
| File | Description |
|---|---|
tests/Aspire.Dashboard.Playwright.Tests/Integration/Playwright/ResourcePaneFilterTests.cs |
Adds short- and long-list scenarios across mobile, tablet, and desktop sizes. |
src/Aspire.Dashboard/Components/Controls/ResourcePaneFilter.razor.css |
Constrains popover placement and height while allowing the filter body to scroll. |
Review effort: Balanced
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
|
Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt. |
Sébastien Ros (sebastienros)
left a comment
There was a problem hiding this comment.
Two things to fix before merge, both in ResourcePaneFilter.razor.css:
display: flexkeeps the panel visible after Escape until Blazor re-renders.max-height: calc(100% - 18px)stopsflip-blockfrom ever firing, so Reset and OK can be clipped when there is little room below the anchor.
The third comment, on a test assertion, is not a blocker.
I tested the CSS in Chromium 152 on a stand-in that copies the structure of Fluent's popover, not on the real dashboard. I did not run the PR's Playwright tests.
Also, the Fixes # (issue) line in the description is still the unfilled placeholder.
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
## Description The resource sidebar is useful for navigation, but its compact presentation makes it difficult to scan a populated application without losing the current filters and selection. This adds an optional full-canvas presentation of the **same resource pane**, plus contextual access to existing resource actions. Relates to #21708 (UX29). - Desktop users can expand the pane, inspect runtime state, start time, endpoints, Source, and activity links, then select a resource to return to details. Filters, grouping, ordering, selection, and the active available details tab are preserved. - Right-click, the Context Menu key, and Shift+F10 open the existing resource-action pipeline for the intended row. Ellipsis/touch actions remain available; command availability, confirmation, and secret handling are unchanged. - Width is controlled only by explicit resizing: 420px by default, an exact saved width within 420-560px, keyboard arrows/Home/End, or double-click to reset to 420px. There is no automatic fitting. Collapse/restore keeps details anchored to the workspace edge. - At **768px and below**, the existing mobile drawer is used and the full-canvas control/state is absent. At **769px and above**, desktop expansion is available. Crossing into mobile exits full canvas without automatically reentering later. - Tightly coupled improvements include neutral selection surfaces, consistent resource tiles and persistent badges, density-aware metadata, single-line platform-specific selection guidance, viewport-contained filter/group menus, and one shared visible-only activity refresh lifecycle. Full canvas is transient and opt-in. This does not change the default layout, add a separate table/cards implementation, or change the independently approved Summary behavior. A no-op `fitPaneToActions` export is deliberately retained for stale asset/assembly compatibility; no C# auto-fit calls remain. ### User-facing usage On desktop, choose **Expand resource list to full canvas**. Use the existing filters and grouping, Ctrl/Command+click to add/remove selection, or Shift+click to select a range. Select a resource to return to details, or use the toolbar control to return to the compact pane. Right-click a row or use Shift+F10 for its existing actions. At narrow sidebar widths, the two selection hints alternate in one stable line; wider/full-canvas panes show both. Rotation pauses on hover/focus and while hidden, and reduced motion removes the crossfade. Assistive technology always receives both instructions. ### Overlap with #21692 #21692 addresses resource-filter viewport containment. This branch includes overlapping filter sizing/containment behavior and extends coverage to full canvas, grouping menus, short viewports, Escape/focus return, and responsive transitions. These are **not independent fixes**: reconcile the overlapping `ResourcePaneFilter` CSS and tests before merging both PRs. This PR does not claim that #21692 has already merged. ### Validation The complete relevant gate passed: **145 component cases, 16 model/action-menu cases, and 130 browser cases**, with zero failures or skips. The browser cases intentionally opt into their outerloop scenarios while excluding quarantined cases. The final visual pass found and corrected a forced-colors text-backplate issue on selected rows. After that CSS-only correction, the **5 targeted selection/hierarchy browser cases passed**, the dashboard rebuilt successfully, and the complete named-TestShop interaction/capture pass was repeated successfully. The earlier full 130-case gate preceded only that forced-colors correction and final whitespace cleanup. Build setup and commands used for the isolated validation outputs: ```powershell $root = Join-Path $PWD 'artifacts\ux29-row-composition-tests' $env:MSBUILDTERMINALLOGGER = 'false' $p = @( "/p:ArtifactsPath=$root", "/p:ArtifactsDir=$root\", '/p:SkipNativeBuild=true', '/m:1', '/p:BuildInParallel=false', '/p:UseSharedCompilation=false', '--verbosity', 'quiet' ) .\dotnet.cmd build tests\Aspire.Dashboard.Components.Tests\Aspire.Dashboard.Components.Tests.csproj @p .\dotnet.cmd build tests\Aspire.Dashboard.Tests\Aspire.Dashboard.Tests.csproj @p .\dotnet.cmd build tests\Aspire.Dashboard.Playwright.Tests\Aspire.Dashboard.Playwright.Tests.csproj @p ``` **Result:** all three builds passed. ```powershell .\dotnet.cmd "$root\bin\Aspire.Dashboard.Components.Tests\Debug\Aspire.Dashboard.Components.Tests.dll" --filter-class '*.ResourcesLayout*' --filter-class '*.ResourceOverview*' --filter-class '*.ResourcePaneFilter*' --filter-class '*.AspireMenuTests' --filter-not-trait 'quarantined=true' --filter-not-trait 'outerloop=true' ``` **Result:** 145/145 passed, 12.577s. ```powershell .\dotnet.cmd "$root\bin\Aspire.Dashboard.Tests\Debug\Aspire.Dashboard.Tests.dll" --filter-class '*.ResourcePaneActivityTests' --filter-class '*.ResourceMenuBuilderTests' --filter-not-trait 'quarantined=true' --filter-not-trait 'outerloop=true' ``` **Result:** 16/16 passed, 0.799s. ```powershell .\dotnet.cmd "$root\bin\Aspire.Dashboard.Playwright.Tests\Debug\Aspire.Dashboard.Playwright.Tests.dll" --filter-class '*.ResourcePane*' --filter-class '*.ResourceTagPaneTests' --filter-class '*.ResourcesTests' --filter-class '*.AspireMenuLayoutTests' --filter-not-trait 'quarantined=true' ``` **Result:** 130/130 passed, 4m23.682s. Includes **48 axe surface audits with zero reported violations**, covering the pane, filter, grouping, and resource context menu across light/dark and 390/768/769/1440px. ```powershell .\dotnet.cmd "$root\bin\Aspire.Dashboard.Playwright.Tests\Debug\Aspire.Dashboard.Playwright.Tests.dll" --filter-method '*.SelectionSurfaces_UseDashboardNeutralTokensAcrossThemes' --filter-method '*.HierarchyAndSource_PreserveTreePathsAndCompleteDisclosure' --filter-not-trait 'quarantined=true' ``` **Result after the forced-colors correction:** 5/5 passed, 11.247s. Additional completed checks: - The resizer lifecycle and available-Traces-tab navigation cases passed **three isolated repetitions, 4/4 each**, including ordinary and reduced motion. Dynamic persistent-badge geometry passed **three repetitions, 2/2 each**; tile overflow is allowed while row containment, icon stability, and non-overlap remain required. - Populated performance fixture: **28 unique resources / 56 tag memberships**. During a 10s idle observation: **0 DOM mutations, 0 long tasks, CLS 0**. Activity update: **448 mutations bounded to the 28 changed resources, 0 long tasks, CLS 0**. Idle/refresh transport observations were respectively 3/7 render frames and 28,060/167,975 bytes. Lifecycle tests cover single-owner refresh, maximum query concurrency 1, cancellation, hidden/mobile transitions, and stale-result rejection. - The final named TestShop dashboard rebuild passed with **0 warnings and 0 errors**. Cache-bypass loading, exact 507px restoration, reset/reload at 420px, full-canvas selection, responsive exit, and ordinary collapse/restore passed. No yellow error bar, browser page/console errors, or post-start server errors were observed. - Actual live collapse and restore were sampled at **20 frames per direction**: right-edge drift **0px**, left edge matched the pane boundary, widths changed monotonically, and no content transform or horizontal overflow occurred. - Keyboard/context invocation, focus return, inert content, reduced motion, forced colors, density geometry, popup containment, activity trace links/Back, and responsive control absence/presence are covered by the browser/component gates. Axe is supplemented by these interaction and visual checks, not treated as a complete accessibility certification. ```powershell .\dotnet.cmd build src\Aspire.Dashboard\Aspire.Dashboard.csproj /t:UpdateXlf @p $changed = @(git diff --cached --name-only -- '*.cs') | ForEach-Object { $_ -replace '/', '\' } .\dotnet.cmd format whitespace --folder --verify-no-changes --include @Changed --verbosity quiet Get-Content src\Aspire.Dashboard\Components\Layout\ResourcesLayout.razor.js -Raw | node --input-type=module --check git diff --cached --check ``` **Result:** localization regeneration, changed-C# whitespace verification, JavaScript syntax, and staged whitespace checks passed. All 13 XLF updates were generated, not manually edited. ### Screenshots / Recordings **Actual named TestShop captures from the deployed revision**, inspected before upload. No credentials, parameter values, or private research artifacts are included. | Desktop full canvas - light | Desktop full canvas - dark | | --- | --- | |  |  | | Mobile drawer - 390x844 | Tablet drawer - 768x1024 | | --- | --- | |  |  | | Desktop boundary - 769px | Forced colors | | --- | --- | |  |  | Motion was verified with the frame-sampled geometry checks above. To inspect interactively, collapse and restore the sidebar, then expand to full canvas and select a resource; repeat with reduced motion enabled. ## Checklist - Is this feature complete? - [x] Yes. Ready to ship. - [ ] No. Follow-up changes expected. - Are you including unit tests for the changes and scenario tests if relevant? - [x] 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 - [x] 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 - [x] No --------- Co-authored-by: David Pine <7679720+IEvangelist@users.noreply.github.com> Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve the upstream filter containment and native dismissal CSS, including its minimum panel height. Keep the responsive regression synchronized using the narrow tab header and verify selected-resource context through the active resource link. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
This comment has been minimized.
This comment has been minimized.
…ement Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Filter regression validation
Functional coverageAll nine browser cases passed, with zero failures and zero skips:
dotnet test --project tests\Aspire.Dashboard.Playwright.Tests\Aspire.Dashboard.Playwright.Tests.csproj --configuration Release --no-launch-profile -- --filter-class '*.ResourcePaneFilterTests' --filter-class '*.ResourcePaneLongFilterTests' --filter-trait 'outerloop=true' --filter-not-trait 'quarantined=true'Result: passed, 9/9. Tests explicitly opt into only the targeted outerloop scenarios and exclude quarantined tests. The initial Debug attempt failed before executing tests because the running TestShop dashboard locked its executable. Release separated the validation artifacts from the retained preview. The build reported an NU1903 advisory for the pinned Microsoft.DotNet.ILCompiler package; no dependency changes were made.
Live inspectionThe user inspected the real TestShop resource Filters popover and approved the feature. At a 453x684 canvas viewport, Reset and OK were visible and inside the viewport. The preview remains running at its existing URL. The grouping-menu selected-item color concern was traced to already-merged PR #21727 and was not changed in this test-only branch. StatusExisting PR reused; no duplicate PR, merge, or Agent merge enablement. New CI triggered by the push is not yet verified by this report. Test-owned browser fixtures were disposed by the test harness; the retained TestShop AppHost was not stopped. Full test output: |
Tests selector0 / 101 PR test projects · 0 PR jobs, from 1 changed file. Selected PR test projects (0 / 101)none — no PR-gated .NET test projects run for this change. Selected PR jobs (0)none How these were chosen — grouped by what changedJob reasonsnone Selection computed for commit |
|
✅ No documentation update needed. docs_optional → proven false positive (test_only). Triggered signals: |

Description
Current scope after main integration: The production containment and native-dismissal changes are now in
mainthrough #21727. Merge commit5b1fcdb5f73ae800c44d436f873fd72f1903b793preserves that upstream CSS exactly, including its 112px minimum panel height. This PR's remaining diff against main is browser regression coverage only. The original implementation rationale and historical evidence are retained below.The resource-pane Filters popover could put Reset and OK outside the viewport on mobile and tablet. Long lists could also push the footer below the window, including on desktop.
The containment approach keeps the existing popover and controls within the viewport, with a content-sized short panel and a scrollable body for long lists. It clears Fluent's inline insets without overriding fallback placement, uses CSS position-area for the actual available space, and keeps the header/footer from shrinking. No new positioning JavaScript or dependencies are required.
Filter keys and values, lifecycle and health semantics, selected-resource context, localization, and controls remain unchanged.
Related issue: N/A (independently measured runtime defect).
User-facing usage
Open a resource, open its resource drawer on mobile/tablet, and select Filters. Change a checkbox, close with OK, reopen, and use Reset. Both actions fit within the viewport; long lists scroll independently of the footer. Collapsing Resource types and State still reduces the short panel from 390px to 290px in the original fixture captures.
Screenshots / Recordings
Long-content mobile state: the list scrolls while the footer remains visible.
Validation
The independently created branch was originally built and validated directly on fresh canonical
upstream/mainat9f0961d0c7173732f45ce88d571c8b53f8059934. At initial publication, fetched main was3d42183549d7fc8e4817e5c98c8d427ebd20e464, one unrelated commit ahead. No other branch's commits were carried into the original proposal. The later merge of current main is documented below.SDK/dependency setup:
.\restore.cmdResult: passed, with existing NuGet advisory warnings.
Initial regression run against unchanged CSS:
Result: failed. Mobile/tablet short panels and all three long panels failed containment; the desktop short case exposed the separate baseline ARIA limitation noted below.
Unchanged short-panel baseline after separating that ARIA expectation from the positioning regression:
Result: 1 passed (desktop), 2 failed (mobile/tablet containment).
Initial full regression run after the CSS containment fix, before the resize-synchronization follow-up, explicitly opting into only these browser outerloop scenarios and excluding quarantined tests:
Historical result: 6 passed, 0 failed, 0 skipped. Review subsequently identified that the resize portion could pass against stale desktop markup before the throttled responsive render. The corrected resize theory was revalidated below; the entire six-case suite was not rerun for the test-only resize follow-up.
The tests check the dialog, filter, and both action bounds, not only document scroll width. Coverage includes real pointer Reset/OK, filtering retained after OK, wheel scrolling through long content, short-panel shrink, desktop/tablet/mobile resizing, Escape/trigger focus, outside dismissal, and unchanged selected-resource context. Temporary capture code was removed; screenshots are attachments, not committed files.
Review follow-up: responsive resize synchronization
Waiting for the actual responsive header exposed the original race: the 1440px-start case failed after resizing to 768px because its drawer was closed, while the two narrow-start cases passed (2 passed, 1 failed). The test-only correction in
40731e2ce160aa714d6b8421fe62f7c5987ee9bewaited for the selected-resource header button to appear or disappear for the requested mode, opened the drawer when needed, and only then checked the Filters trigger and popover bounds. Production containment behavior was unchanged.The same focused command was run once for the synchronized baseline and once after adding the conditional drawer open:
Corrected result: 3 passed, 0 failed, 0 skipped. No unrelated tests were rerun for that follow-up.
Review follow-up: native dismissal and short windows
Correction in
51f9158e53e395270c9afef9185ac932d79fbaa8: apply flex display only to the native open popover, and prefer the anchor placement with the most height. This preserves native immediate dismissal and keeps the existing body-only scrolling layout; it does not add whole-panel scrolling or change focus policy. Removed two vacuous focus assertions while retaining the real Escape focus check.New actual-dashboard regressions synchronously measure the dialog immediately after native
hidePopover(), before a server render, and check short windows at 1440×300 and 800×200. The short-window tests also change a checkbox and use Reset/OK through pointer actions.The first attempt failed during resource/navigation setup (3 failures), not on the CSS assertions. The single-resource tests now explicitly provide their synthetic resource through the existing fixture extension point rather than relying on the mock client's empty default repository. With that fixture and unchanged production CSS: 1 passed, 2 failed (native dismissal and 800×200 footer containment). After the CSS correction: 3 passed, 0 failed.
Affected-class validation, including the original six scenarios and strengthened short-window pointer interactions:
Result before main integration: 9 passed, 0 failed, 0 skipped.
git diff --checkpassed.Main integration and conflict resolution
Merged freshly fetched
upstream/mainat015591f4796a5a3c9c329f687800205355304bf1, retaining upstream's production filter CSS unchanged. Upstream now hides the resource-title button while the mobile drawer is open. The regression waits for the narrow header's tab container instead, opens the drawer only if hidden, and verifies selected-resource context through the active resource link plus unchanged URL..\restore.cmdResult: passed, 17 package-advisory warnings, 0 errors.
Before adapting the assertions to upstream's header behavior: 4 passed, 5 failed. After adaptation, on merge commit
5b1fcdb5f73ae800c44d436f873fd72f1903b793: 9 passed, 0 failed, 0 skipped.Result: passed. All test-owned pages, browser contexts, and dashboard fixtures were disposed; observed fixture ports are closed. No retained preview service was modified.
Observed baseline limitation
On unchanged desktop code, Reset restored the resource rows, but a Fluent checkbox could retain
aria-checked="false"whilechecked="true". That pre-existing accessibility mismatch is not fixed by this containment work. The retained regression verifies Reset through the restored rows and disabled Reset action rather than claiming the ARIA mismatch is resolved. Existing OK focus behavior is also preserved; Escape returns focus to the trigger.Checklist
<remarks />and<code />elements on your triple slash comments?