From 2ac604e671cc9b743886a14abf2130de68088f5b Mon Sep 17 00:00:00 2001 From: James Crutchley Date: Wed, 29 Jan 2025 10:19:04 -0800 Subject: [PATCH 1/3] Fix XamlRoot being set incorrectly in Popup Handler (#2476) * Fix XamlRoot being set incorrectly in Popup Handler when using modal navigation in Windows * Updated `PopupSizeConstants` to not make sample app popup size too large when testing Mulitple popups page --- samples/CommunityToolkit.Maui.Sample/Models/PopupSize.cs | 4 ++-- .../Handlers/Popup/PopUpHandler.windows.cs | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/samples/CommunityToolkit.Maui.Sample/Models/PopupSize.cs b/samples/CommunityToolkit.Maui.Sample/Models/PopupSize.cs index b8e21cebfe..88cef5a44d 100644 --- a/samples/CommunityToolkit.Maui.Sample/Models/PopupSize.cs +++ b/samples/CommunityToolkit.Maui.Sample/Models/PopupSize.cs @@ -6,8 +6,8 @@ public PopupSizeConstants(IDeviceDisplay deviceDisplay) { Tiny = new(100, 100); Small = new(300, 300); - Medium = new(0.7 * (deviceDisplay.MainDisplayInfo.Width / deviceDisplay.MainDisplayInfo.Density), 0.6 * (deviceDisplay.MainDisplayInfo.Height / deviceDisplay.MainDisplayInfo.Density)); - Large = new(0.9 * (deviceDisplay.MainDisplayInfo.Width / deviceDisplay.MainDisplayInfo.Density), 0.8 * (deviceDisplay.MainDisplayInfo.Height / deviceDisplay.MainDisplayInfo.Density)); + Medium = new(0.4 * (deviceDisplay.MainDisplayInfo.Width / deviceDisplay.MainDisplayInfo.Density), 0.6 * (deviceDisplay.MainDisplayInfo.Height / deviceDisplay.MainDisplayInfo.Density)); + Large = new(0.5 * (deviceDisplay.MainDisplayInfo.Width / deviceDisplay.MainDisplayInfo.Density), 0.8 * (deviceDisplay.MainDisplayInfo.Height / deviceDisplay.MainDisplayInfo.Density)); } // examples for fixed sizes diff --git a/src/CommunityToolkit.Maui.Core/Handlers/Popup/PopUpHandler.windows.cs b/src/CommunityToolkit.Maui.Core/Handlers/Popup/PopUpHandler.windows.cs index 132bab03f1..0ea2845638 100644 --- a/src/CommunityToolkit.Maui.Core/Handlers/Popup/PopUpHandler.windows.cs +++ b/src/CommunityToolkit.Maui.Core/Handlers/Popup/PopUpHandler.windows.cs @@ -41,9 +41,8 @@ public static void MapOnOpened(PopupHandler handler, IPopup view, object? result ArgumentNullException.ThrowIfNull(handler.MauiContext); var parent = view.Parent.ToPlatform(handler.MauiContext); - parent.IsHitTestVisible = false; - handler.PlatformView.XamlRoot = parent.XamlRoot; + handler.PlatformView.XamlRoot = view.GetWindow().Content?.Handler?.MauiContext?.GetPlatformWindow().Content.XamlRoot ?? throw new InvalidOperationException("Window Content cannot be null"); handler.PlatformView.IsHitTestVisible = true; handler.PlatformView.IsOpen = true; From 0bf5414810e50ae248cc53eecdb2747f259f3f49 Mon Sep 17 00:00:00 2001 From: Brandon Minnick <13558917+TheCodeTraveler@users.noreply.github.com> Date: Wed, 29 Jan 2025 11:34:55 -0800 Subject: [PATCH 2/3] Add `SupportedOSPlatform` to reduce compiler warnings --- .../AppBuilderExtensions.shared.cs | 8 +++++++- .../Primitives/CameraViewDefaults.shared.cs | 6 ++++++ .../Views/CameraView.shared.cs | 1 + 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/CommunityToolkit.Maui.Camera/AppBuilderExtensions.shared.cs b/src/CommunityToolkit.Maui.Camera/AppBuilderExtensions.shared.cs index d38710c0a2..c99bfa3781 100644 --- a/src/CommunityToolkit.Maui.Camera/AppBuilderExtensions.shared.cs +++ b/src/CommunityToolkit.Maui.Camera/AppBuilderExtensions.shared.cs @@ -1,4 +1,5 @@ -using CommunityToolkit.Maui.Core; +using System.Runtime.Versioning; +using CommunityToolkit.Maui.Core; using CommunityToolkit.Maui.Core.Handlers; using CommunityToolkit.Maui.Views; @@ -7,6 +8,11 @@ namespace CommunityToolkit.Maui; /// /// This class contains CameraView's extensions. /// +[SupportedOSPlatform("windows10.0.10240.0")] +[SupportedOSPlatform("android21.0")] +[SupportedOSPlatform("ios")] +[SupportedOSPlatform("maccatalyst")] +[SupportedOSPlatform("tizen")] public static class AppBuilderExtensions { /// diff --git a/src/CommunityToolkit.Maui.Camera/Primitives/CameraViewDefaults.shared.cs b/src/CommunityToolkit.Maui.Camera/Primitives/CameraViewDefaults.shared.cs index 3d76a9ae7c..dca577d59a 100644 --- a/src/CommunityToolkit.Maui.Camera/Primitives/CameraViewDefaults.shared.cs +++ b/src/CommunityToolkit.Maui.Camera/Primitives/CameraViewDefaults.shared.cs @@ -1,10 +1,16 @@ using System.ComponentModel; +using System.Runtime.Versioning; using System.Windows.Input; using CommunityToolkit.Maui.Core.Primitives; using CommunityToolkit.Maui.Views; namespace CommunityToolkit.Maui.Core; /// Default Values for "/> +[SupportedOSPlatform("windows10.0.10240.0")] +[SupportedOSPlatform("android21.0")] +[SupportedOSPlatform("ios")] +[SupportedOSPlatform("maccatalyst")] +[SupportedOSPlatform("tizen")] [EditorBrowsable(EditorBrowsableState.Never)] public static class CameraViewDefaults { diff --git a/src/CommunityToolkit.Maui.Camera/Views/CameraView.shared.cs b/src/CommunityToolkit.Maui.Camera/Views/CameraView.shared.cs index 84e0db8da1..d2b010cac9 100644 --- a/src/CommunityToolkit.Maui.Camera/Views/CameraView.shared.cs +++ b/src/CommunityToolkit.Maui.Camera/Views/CameraView.shared.cs @@ -14,6 +14,7 @@ namespace CommunityToolkit.Maui.Views; [SupportedOSPlatform("android21.0")] [SupportedOSPlatform("ios")] [SupportedOSPlatform("maccatalyst")] +[SupportedOSPlatform("tizen")] public partial class CameraView : View, ICameraView { static readonly BindablePropertyKey isAvailablePropertyKey = From d694f5f11d66c15718886c01d370089347822436 Mon Sep 17 00:00:00 2001 From: Brandon Minnick <13558917+TheCodeTraveler@users.noreply.github.com> Date: Wed, 29 Jan 2025 15:31:40 -0800 Subject: [PATCH 3/3] [Housekeeping] Fix Unit Test Failure: `Catastrophic failure: System.ArgumentOutOfRangeException` (#2479) * Update tests * Use `Assert.ThrowsAnyAsync` --------- Co-authored-by: Brandon Minnick <13558917+brminnick@users.noreply.github.com> --- .github/workflows/dotnet-build.yml | 2 +- ...ityToolkit.Maui.Analyzers.UnitTests.csproj | 1 + .../CommunityToolkit.Maui.UnitTests.csproj | 1 + .../Views/Popup/PopupTests.cs | 68 ++++++------------- .../Views/Popup/Popup.shared.cs | 2 + 5 files changed, 25 insertions(+), 49 deletions(-) diff --git a/.github/workflows/dotnet-build.yml b/.github/workflows/dotnet-build.yml index d556bbbaac..5f9b745950 100644 --- a/.github/workflows/dotnet-build.yml +++ b/.github/workflows/dotnet-build.yml @@ -190,7 +190,7 @@ jobs: run: dotnet build ${{ env.PathToLibrarySolution }} -c Release -p:PackageVersion=${{ env.NugetPackageVersion }} -p:Version=${{ env.NugetPackageVersion }} - name: Run All Unit Tests - run: dotnet test -c Release ${{ env.PathToLibrarySolution }} --settings ".runsettings" --collect "XPlat code coverage" --logger trx --results-directory ${{ runner.temp }} + run: dotnet test -c Release ${{ env.PathToLibrarySolution }} --settings ".runsettings" --collect "XPlat code coverage" --logger trx --results-directory ${{ runner.temp }} --logger GitHubActions - name: Publish Test Results if: runner.os == 'Windows' diff --git a/src/CommunityToolkit.Maui.Analyzers.UnitTests/CommunityToolkit.Maui.Analyzers.UnitTests.csproj b/src/CommunityToolkit.Maui.Analyzers.UnitTests/CommunityToolkit.Maui.Analyzers.UnitTests.csproj index 20c5766db2..5704417b12 100644 --- a/src/CommunityToolkit.Maui.Analyzers.UnitTests/CommunityToolkit.Maui.Analyzers.UnitTests.csproj +++ b/src/CommunityToolkit.Maui.Analyzers.UnitTests/CommunityToolkit.Maui.Analyzers.UnitTests.csproj @@ -20,6 +20,7 @@ + diff --git a/src/CommunityToolkit.Maui.UnitTests/CommunityToolkit.Maui.UnitTests.csproj b/src/CommunityToolkit.Maui.UnitTests/CommunityToolkit.Maui.UnitTests.csproj index 10fa0064f3..d0d74d952a 100644 --- a/src/CommunityToolkit.Maui.UnitTests/CommunityToolkit.Maui.UnitTests.csproj +++ b/src/CommunityToolkit.Maui.UnitTests/CommunityToolkit.Maui.UnitTests.csproj @@ -17,6 +17,7 @@ + diff --git a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupTests.cs b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupTests.cs index 9a16351207..1a622c3f9f 100644 --- a/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupTests.cs +++ b/src/CommunityToolkit.Maui.UnitTests/Views/Popup/PopupTests.cs @@ -9,10 +9,12 @@ namespace CommunityToolkit.Maui.UnitTests.Views; public class PopupTests : BaseHandlerTest { const string resultWhenUserTapsOutsideOfPopup = "User Tapped Outside of Popup"; - readonly IPopup popup = new MockPopup(); + readonly MockPopup popup = new(); + readonly MockPopupHandler popupHandler; public PopupTests() { + popupHandler = CreateElementHandler(popup); Assert.IsType(new MockPopup(), exactMatch: false); } @@ -49,15 +51,13 @@ public async Task ShowPopupAsync_CancellationTokenExpired() app.Windows[0].Page = page; - var popupHandler = CreateElementHandler(popup); - Assert.NotNull(popup.Handler); Assert.NotNull(page.Handler); // Ensure CancellationToken Has Expired await Task.Delay(100, CancellationToken.None); - await Assert.ThrowsAsync(() => page.ShowPopupAsync((MockPopup)popup, cts.Token)); + await Assert.ThrowsAnyAsync(() => page.ShowPopupAsync(popup, cts.Token)); } [Fact(Timeout = (int)TestDuration.Short)] @@ -80,15 +80,13 @@ public async Task ShowPopupAsync_CancellationTokenCancelled() app.Windows[0].Page = page; - var popupHandler = CreateElementHandler(popup); - Assert.NotNull(popup.Handler); Assert.NotNull(page.Handler); // Ensure CancellationToken Has Expired await cts.CancelAsync(); - await Assert.ThrowsAsync(() => page.ShowPopupAsync((MockPopup)popup, cts.Token)); + await Assert.ThrowsAnyAsync(() => page.ShowPopupAsync(popup, cts.Token)); } [Fact(Timeout = (int)TestDuration.Short)] @@ -111,15 +109,13 @@ public async Task CloseAsync_CancellationTokenExpired() app.Windows[0].Page = page; - var popupHandler = CreateElementHandler(popup); - Assert.NotNull(popup.Handler); Assert.NotNull(page.Handler); // Ensure CancellationToken Has Expired await Task.Delay(100, CancellationToken.None); - await Assert.ThrowsAsync(() => ((MockPopup)popup).CloseAsync(token: cts.Token)); + await Assert.ThrowsAnyAsync(() => popup.CloseAsync(token: cts.Token)); } [Fact(Timeout = (int)TestDuration.Short)] @@ -142,15 +138,13 @@ public async Task CloseAsync_CancellationTokenCancelled() app.Windows[0].Page = page; - var popupHandler = CreateElementHandler(popup); - Assert.NotNull(popup.Handler); Assert.NotNull(page.Handler); // Ensure CancellationToken Has Expired await cts.CancelAsync(); - await Assert.ThrowsAsync(() => ((MockPopup)popup).CloseAsync(token: cts.Token)); + await Assert.ThrowsAnyAsync(() => popup.CloseAsync(token: cts.Token)); } [Fact(Timeout = (int)TestDuration.Short)] @@ -171,17 +165,15 @@ public async Task OnOpenedMapperIsCalled() app.Windows[0].Page = page; - var popupHandler = CreateElementHandler(popup); - Assert.NotNull(popup.Handler); Assert.NotNull(page.Handler); - page.ShowPopup((MockPopup)popup); + page.ShowPopup(popup); Assert.Equal(1, popupHandler.OnOpenedCount); - popup.OnDismissedByTappingOutsideOfPopup(); + await popup.OnDismissedByTappingOutsideOfPopup(CancellationToken.None); - var popupTask = page.ShowPopupAsync((MockPopup)popup, CancellationToken.None); - popup.OnDismissedByTappingOutsideOfPopup(); + var popupTask = page.ShowPopupAsync(popup, CancellationToken.None); + await popup.OnDismissedByTappingOutsideOfPopup(CancellationToken.None); await popupTask; @@ -202,7 +194,7 @@ public async Task PopupDismissedByTappingOutsideOfPopup() } }; - ((MockPopup)popup).Closed += (s, e) => + popup.Closed += (s, e) => { Assert.Equal(popup, s); popupClosedTCS.SetResult(((string?)e.Result, e.WasDismissedByTappingOutsideOfPopup)); @@ -213,12 +205,10 @@ public async Task PopupDismissedByTappingOutsideOfPopup() app.Windows[0].Page = page; - CreateElementHandler(popup); - Assert.NotNull(popup.Handler); Assert.NotNull(page.Handler); - popup.OnDismissedByTappingOutsideOfPopup(); + await popup.OnDismissedByTappingOutsideOfPopup(CancellationToken.None); var (result, wasDismissedByTappingOutsideOfPopup) = await popupClosedTCS.Task; @@ -247,20 +237,17 @@ public async Task OnDismissedWithResult() app.Windows[0].Page = page; - // Make sure that our popup will have a Handler - CreateElementHandler(popup); - Assert.NotNull(popup.Handler); Assert.NotNull(page.Handler); - ((MockPopup)popup).Closed += (_, e) => + popup.Closed += (_, e) => { result = e.Result; isPopupDismissed = true; - closedTCS.TrySetResult(); + closedTCS.SetResult(); }; - ((MockPopup)popup).Close(new object()); + popup.Close(new object()); await closedTCS.Task; Assert.True(isPopupDismissed); @@ -288,19 +275,16 @@ public async Task OnDismissedWithoutResult() app.Windows[0].Page = page; - // Make sure that our popup will have a Handler - CreateElementHandler(popup); - Assert.NotNull(popup.Handler); Assert.NotNull(page.Handler); - ((MockPopup)popup).Closed += (_, e) => + popup.Closed += (_, e) => { result = e.Result; isPopupDismissed = true; }; - await ((MockPopup)popup).CloseAsync(token: CancellationToken.None); + await popup.CloseAsync(token: CancellationToken.None); Assert.True(isPopupDismissed); Assert.Null(result); @@ -341,17 +325,14 @@ public async Task ShowPopup_IsLogicalChild() app.Windows[0].Page = page; - // Make sure that our popup will have a Handler - CreateElementHandler(popup); - Assert.NotNull(popup.Handler); Assert.NotNull(page.Handler); Assert.Single(page.LogicalChildrenInternal); - page.ShowPopup((MockPopup)popup); + page.ShowPopup(popup); Assert.Equal(2, page.LogicalChildrenInternal.Count); - await ((MockPopup)popup).CloseAsync(token: CancellationToken.None); + await popup.CloseAsync(token: CancellationToken.None); Assert.Single(page.LogicalChildrenInternal); } @@ -361,15 +342,6 @@ public MockPopup() { ResultWhenUserTapsOutsideOfPopup = resultWhenUserTapsOutsideOfPopup; } - - protected override async Task OnClosed(object? result, bool wasDismissedByTappingOutsideOfPopup, CancellationToken token = default) - { - await Task.Delay(100, token); - - ((IPopup)this).HandlerCompleteTCS.TrySetResult(); - - await base.OnClosed(result, wasDismissedByTappingOutsideOfPopup, token); - } } sealed class PopupViewModel : INotifyPropertyChanged diff --git a/src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs b/src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs index 5c058b773e..2099c179ed 100644 --- a/src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs +++ b/src/CommunityToolkit.Maui/Views/Popup/Popup.shared.cs @@ -343,6 +343,8 @@ internal virtual void OnOpened() => /// protected virtual async Task OnClosed(object? result, bool wasDismissedByTappingOutsideOfPopup, CancellationToken token = default) { + token.ThrowIfCancellationRequested(); + ((IPopup)this).OnClosed(result); ((IResourceDictionary)resources).ValuesChanged -= OnResourcesChanged;