LineHeight and decorations for HTML Label - fix#31202
LineHeight and decorations for HTML Label - fix#31202kubaflo merged 6 commits intodotnet:inflight/currentfrom
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR fixes issues with LineHeight, TextDecorations, and CharacterSpacing not being applied to HTML labels in .NET MAUI. The fix corrects the mapping logic to allow these properties to work with HTML text type labels, not just plain text labels.
- Updates the mapper conditions to check for FormattedTextSpans instead of plain text, allowing HTML labels to receive these style properties
- Modifies the iOS handler to refresh LineHeight, TextDecorations, and CharacterSpacing when TextType changes
- Adds comprehensive test case to verify the fix works correctly
Reviewed Changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
src/Controls/src/Core/Label/Label.Mapper.cs |
Updates mapping conditions from IsPlainText check to HasFormattedTextSpans check for LineHeight, TextDecorations, and CharacterSpacing |
src/Controls/src/Core/Label/Label.iOS.cs |
Adds refresh calls for LineHeight, TextDecorations, and CharacterSpacing when TextType changes in iOS handler |
src/Controls/tests/TestCases.HostApp/Issues/Issue22197.xaml |
Creates UI test page demonstrating HTML vs plain text labels with styling properties |
src/Controls/tests/TestCases.HostApp/Issues/Issue22197.xaml.cs |
Code-behind for the test page |
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22197.cs |
NUnit test implementation to verify the fix through screenshot verification |
| LineHeight="2"> | ||
| </Label> | ||
| <Label | ||
| AutomationId="label" |
There was a problem hiding this comment.
The AutomationId 'label' is too generic and could conflict with other elements. Consider using a more specific identifier like 'htmlTextTypeLabel' to ensure uniqueness across all test cases.
|
Hey there @@kubaflo! Thank you so much for your PR! Someone from the team will get assigned to your PR shortly and we'll get it reviewed. |
| LineHeight="2" | ||
| Text="Etiam sodales sollicitudin diam, vel tincidunt libero eleifend id. Vestibulum vehicula congue velit, id egestas nulla pellentesque at."/> | ||
| <Label Text="Plain Text type"/> | ||
| </VerticalStackLayout> |
There was a problem hiding this comment.
This would exercise the line height feature more:
| </VerticalStackLayout> | |
| <!-- Exercise line heights less than 1. --> | |
| <Label BackgroundColor="Green" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| <Label BackgroundColor="Red" LineHeight="0.8" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| <Label BackgroundColor="Green" LineHeight="0.2" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| <Label BackgroundColor="Red" LineHeight="0.4" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| <Label BackgroundColor="Green" LineHeight="1.0" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| <Label BackgroundColor="Red" LineHeight="1.2" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| <Label BackgroundColor="Green" LineHeight="1.4" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| <Label BackgroundColor="Red" LineHeight="1.6" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| <Label BackgroundColor="Green" LineHeight="2.0" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| <Label BackgroundColor="Red" LineHeight="4.6" FontSize="20" Text="ÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑÑ"/> | |
| </VerticalStackLayout> |
See comment #24520 (comment)
There was a problem hiding this comment.
Hmmm but this pr is not about lineHeight in this sense. It is about the line Height for HTML labels
|
🚀 Dogfood this PR with:
curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 31202Or
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 31202" |
…otnet#34548) <!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description Adds a [gh-aw (GitHub Agentic Workflows)](https://github.github.com/gh-aw/introduction/overview/) workflow that automatically evaluates test quality on PRs using the `evaluate-pr-tests` skill. ### What it does When a PR adds or modifies test files, this workflow: 1. **Checks out the PR branch** (including fork PRs) in a pre-agent step 2. **Runs the `evaluate-pr-tests` skill** via Copilot CLI in a sandboxed container 3. **Posts the evaluation report** as a PR comment using gh-aw safe-outputs ### Triggers | Trigger | When | Fork PR support | |---------|------|-----------------| | `pull_request` | Automatic on test file changes (`src/**/tests/**`) | ❌ Blocked by `pre_activation` gate | | `workflow_dispatch` | Manual — enter PR number | ✅ Works for all PRs | | `issue_comment` (`/evaluate-tests`) | Comment on PR |⚠️ Same-repo only (see Known Limitations) | ### Security model | Layer | Implementation | |-------|---------------| | **gh-aw sandbox** | Agent runs in container with scrubbed credentials, network firewall | | **Safe outputs** | Max 1 PR comment per run, content-limited | | **Checkout without execution** | `steps:` checks out PR code but never executes workspace scripts | | **Base branch restoration** | `.github/skills/`, `.github/instructions/`, `.github/copilot-instructions.md` restored from base branch after checkout | | **Fork PR activation gate** | `pull_request` events blocked for forks via `head.repo.id == repository_id` | | **Pinned actions** | SHA-pinned `actions/checkout`, `actions/github-script`, etc. | | **Minimal permissions** | Each job declares only what it needs | | **Concurrency** | One evaluation per PR, cancels in-progress | | **Threat detection** | gh-aw built-in threat detection analyzes agent output | ### Files added/modified - `.github/workflows/copilot-evaluate-tests.md` — gh-aw workflow source - `.github/workflows/copilot-evaluate-tests.lock.yml` — Compiled workflow (auto-generated by `gh aw compile`) - `.github/skills/evaluate-pr-tests/scripts/Gather-TestContext.ps1` — Test context gathering script (binary-safe file download, path traversal protection) - `.github/instructions/gh-aw-workflows.instructions.md` — Copilot instructions for gh-aw development ### Known Limitations **Fork PR evaluation via `/evaluate-tests` comment is not supported in v1.** The gh-aw platform inserts a `checkout_pr_branch.cjs` step after all user steps, which may overwrite base-branch skill files restored for fork PRs. This is a known gh-aw platform limitation — user steps always run before platform-generated steps, with no way to insert steps after. **Workaround:** Use `workflow_dispatch` (Actions UI → "Run workflow" → enter PR number) to evaluate fork PRs. This trigger bypasses the platform checkout step entirely and works correctly. **Related upstream issues:** - [github/gh-aw#18481](github/gh-aw#18481) — "Using gh-aw in forks of repositories" - [github/gh-aw#18518](github/gh-aw#18518) — Fork detection and warning in `gh aw init` - [github/gh-aw#18520](github/gh-aw#18520) — Fork context hint in failure messages - [github/gh-aw#18521](github/gh-aw#18521) — Fork support documentation ### Fixes - Fixes dotnet#34602 --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Co-authored-by: Jakub Florkowski <kubaflo123@gmail.com>
## Summary Enables the copilot-evaluate-tests gh-aw workflow to run on fork PRs by adding `forks: ["*"]` to the `pull_request` trigger and removing the fork guard from `Checkout-GhAwPr.ps1`. ## Changes 1. **copilot-evaluate-tests.md**: Added `forks: ["*"]` to opt out of gh-aw auto-injected fork activation guard. Scoped `Checkout-GhAwPr.ps1` step to `workflow_dispatch` only (redundant for other triggers since platform handles checkout). 2. **copilot-evaluate-tests.lock.yml**: Recompiled via `gh aw compile` — fork guard removed from activation `if:` conditions. 3. **Checkout-GhAwPr.ps1**: Removed the `isCrossRepository` fork guard. Updated header docs and restore comments to accurately describe behavior for all trigger×fork combinations (including corrected step ordering). 4. **gh-aw-workflows.instructions.md**: Updated all stale references to the removed fork guard. Documented `forks: ["*"]` opt-in, clarified residual risk model for fork PRs, and updated troubleshooting table. ## Security Model Fork PRs are safe because: - Agent runs in **sandboxed container** with all credentials scrubbed - Output limited to **1 comment** via `safe-outputs: add-comment: max: 1` - Agent **prompt comes from base branch** (`runtime-import`) — forks cannot alter instructions - Pre-flight check catches missing `SKILL.md` if fork isn't rebased on `main` - No workspace code is executed with `GITHUB_TOKEN` (checkout without execution) ## Testing - ✅ `workflow_dispatch` tested against fork PR dotnet#34621 - ✅ Lock.yml statically verified — fork guard removed from `if:` conditions - ⏳ `pull_request` trigger on fork PRs can only be verified post-merge (GitHub Actions reads lock.yml from default branch) --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…taType is compiled (dotnet#34717) ## Description Adds regression tests for dotnet#34713 verifying the XAML source generator correctly handles bindings with `Converter={StaticResource ...}` inside `x:DataType` scopes. Closes dotnet#34713 ## Investigation After thorough investigation of the source generator pipeline (`KnownMarkups.cs`, `CompiledBindingMarkup.cs`, `NodeSGExtensions.cs`): ### When converter IS in page resources (compile-time resolution ✅) `GetResourceNode()` walks the XAML tree, finds the converter resource, and `ProvideValueForStaticResourceExtension` returns the variable directly — **no runtime `ProvideValue` call**. The converter is referenced at compile time. ### When converter is NOT in page resources (runtime resolution ✅) `GetResourceNode()` returns null → falls through to `IsValueProvider` → generates `StaticResourceExtension.ProvideValue(serviceProvider)`. The `SimpleValueTargetProvider` provides the full parent chain, and `TryGetApplicationLevelResource` checks `Application.Current.Resources`. The binding IS still compiled into a `TypedBinding` — only the converter resolution is deferred. ### Verified on both `main` and `net11.0` All tests pass on both branches. ## Tests added | Test | What it verifies | |------|-----------------| | `SourceGenResolvesConverterAtCompileTime_ImplicitResources` | Converter in implicit `<Resources>` → compile-time resolution, no `ProvideValue` | | `SourceGenResolvesConverterAtCompileTime_ExplicitResourceDictionary` | Converter in explicit `<ResourceDictionary>` → compile-time resolution, no `ProvideValue` | | `SourceGenCompilesBindingWithConverterToTypedBinding` | Converter NOT in page resources → still compiled to `TypedBinding`, no raw `Binding` fallback | | `BindingWithConverterFromAppResourcesWorksCorrectly` × 3 | Runtime behavior correct for all inflators (Runtime, XamlC, SourceGen) | Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
<!-- Please let the below note in for people that find this PR --> > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Description Adds arcade inter-branch merge workflow and configuration to automate merging `net11.0` into the `release/11.0.1xx-preview3` branch. ### Files added | File | Purpose | |------|---------| | `github-merge-flow-release-11.jsonc` | Merge flow config — source `net11.0`, target `release/11.0.1xx-preview3` | | `.github/workflows/merge-net11-to-release.yml` | GitHub Actions workflow — triggers on push to net11.0, daily cron, manual dispatch | ### How it works Uses the shared [dotnet/arcade inter-branch merge infrastructure](https://github.com/dotnet/arcade/blob/main/.github/workflows/inter-branch-merge-base.yml): - **Event-driven**: triggers on push to `net11.0`, with daily cron safety net - **ResetToTargetPaths**: auto-resets `global.json`, `NuGet.config`, `eng/Version.Details.xml`, `eng/Versions.props`, `eng/common/*` to target branch versions - **QuietComments**: reduces GitHub notification noise - Skips PRs when only Maestro bot commits exist ### Incrementing for future releases When cutting a new release (e.g., preview4), update: 1. `github-merge-flow-release-11.jsonc` → change `MergeToBranch` value 2. `.github/workflows/merge-net11-to-release.yml` → update workflow `name` field Follows the same pattern as `merge-main-to-net11.yml` / `github-merge-flow-net11.jsonc`. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Corrects mapping logic for LineHeight, TextDecorations, and CharacterSpacing to apply to HTML labels. Updates iOS handler to refresh these properties when TextType changes. Adds test case for Issue22197 to verify correct rendering of HTML labels with these properties.
|
/azp run maui-pr-uitests |
|
Azure Pipelines successfully started running 1 pipeline(s). |
Code Review — PR #31202Independent AssessmentWhat this changes: Corrects the mapper guard logic for Inferred motivation: Users setting Reconciliation with PR NarrativeAuthor claims: Fixes #22193 and #22197 — LineHeight/TextDecorations/CharacterSpacing not applied to HTML labels. Findings
|
🟢 .NET MAUI Review - ApprovedExpand Full Review -
|
🚦 Gate - Test Before and After Fix📊 Expand Full Gate —
|
| Test | Without Fix (expect FAIL) | With Fix (expect PASS) |
|---|---|---|
🖥️ Issue22197 Issue22197 |
✅ FAIL — 671s | ✅ PASS — 511s |
🔴 Without fix — 🖥️ Issue22197: FAIL ✅ · 671s
Determining projects to restore...
Restored /home/vsts/work/1/s/src/BlazorWebView/src/Maui/Microsoft.AspNetCore.Components.WebView.Maui.csproj (in 9.37 sec).
Restored /home/vsts/work/1/s/src/Controls/src/Core/Controls.Core.csproj (in 9.32 sec).
Restored /home/vsts/work/1/s/src/Controls/Foldable/src/Controls.Foldable.csproj (in 456 ms).
Restored /home/vsts/work/1/s/src/Graphics/src/Graphics/Graphics.csproj (in 29 ms).
Restored /home/vsts/work/1/s/src/Essentials/src/Essentials.csproj (in 34 ms).
Restored /home/vsts/work/1/s/src/Core/src/Core.csproj (in 90 ms).
Restored /home/vsts/work/1/s/src/Core/maps/src/Maps.csproj (in 42 ms).
Restored /home/vsts/work/1/s/src/Controls/Maps/src/Controls.Maps.csproj (in 907 ms).
Restored /home/vsts/work/1/s/src/Controls/src/Xaml/Controls.Xaml.csproj (in 55 ms).
Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.HostApp/Controls.TestCases.HostApp.csproj (in 534 ms).
1 of 11 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:08:11.37
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Determining projects to restore...
Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils/VisualTestUtils.csproj (in 1.17 sec).
Restored /home/vsts/work/1/s/src/Controls/tests/CustomAttributes/Controls.CustomAttributes.csproj (in 48 ms).
Restored /home/vsts/work/1/s/src/TestUtils/src/VisualTestUtils.MagickNet/VisualTestUtils.MagickNet.csproj (in 5.15 sec).
Restored /home/vsts/work/1/s/src/Controls/tests/TestCases.Android.Tests/Controls.TestCases.Android.Tests.csproj (in 6.4 sec).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Core/UITest.Core.csproj (in 2 ms).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Appium/UITest.Appium.csproj (in 2 ms).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.NUnit/UITest.NUnit.csproj (in 328 ms).
Restored /home/vsts/work/1/s/src/TestUtils/src/UITest.Analyzers/UITest.Analyzers.csproj (in 2.07 sec).
5 of 13 projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.10] Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.29] Discovered: Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 04/01/2026 22:39:01 FixtureSetup for Issue22197(Android)
>>>>> 04/01/2026 22:39:04 LineHeightWithHTMLLabelShouldWork Start
>>>>> 04/01/2026 22:39:10 LineHeightWithHTMLLabelShouldWork Stop
>>>>> 04/01/2026 22:39:10 Log types: logcat, bugreport, server
Failed LineHeightWithHTMLLabelShouldWork [6 s]
Error Message:
VisualTestUtils.VisualTestFailedException :
Snapshot different than baseline: LineHeightWithHTMLLabelShouldWork.png (6.10% difference)
If the correct baseline has changed (this isn't a a bug), then update the baseline image.
See test attachment or download the build artifacts to get the new snapshot file.
More info: https://aka.ms/visual-test-workflow
Stack Trace:
at VisualTestUtils.VisualRegressionTester.Fail(String message) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 162
at VisualTestUtils.VisualRegressionTester.VerifyMatchesSnapshot(String name, ImageSnapshot actualImage, String environmentName, ITestContext testContext) in /_/src/TestUtils/src/VisualTestUtils/VisualRegressionTester.cs:line 123
at Microsoft.Maui.TestCases.Tests.UITest.<VerifyScreenshot>g__Verify|13_0(String name, <>c__DisplayClass13_0&) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 477
at Microsoft.Maui.TestCases.Tests.UITest.VerifyScreenshot(String name, Nullable`1 retryDelay, Nullable`1 retryTimeout, Int32 cropLeft, Int32 cropRight, Int32 cropTop, Int32 cropBottom, Double tolerance) in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 309
at Microsoft.Maui.TestCases.Tests.Issues.Issue22197.LineHeightWithHTMLLabelShouldWork() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue22197.cs:line 18
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
NUnit Adapter 4.5.0.0: Test execution complete
Total tests: 1
Failed: 1
Test Run Failed.
Total time: 32.9312 Seconds
🟢 With fix — 🖥️ Issue22197: PASS ✅ · 511s
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0-android36.0/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0-android36.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0-android36.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Maps -> /home/vsts/work/1/s/artifacts/bin/Maps/Debug/net10.0-android36.0/Microsoft.Maui.Maps.dll
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0-android36.0/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.Xaml/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Xaml.dll
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.Foldable/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Foldable.dll
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.Maps/Debug/net10.0-android36.0/Microsoft.Maui.Controls.Maps.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Microsoft.AspNetCore.Components.WebView.Maui/Debug/net10.0-android36.0/Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.TestCases.HostApp -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Controls.TestCases.HostApp.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Graphics -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Graphics.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Essentials -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Maps.dll
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Microsoft.AspNetCore.Components.WebView.Maui -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.AspNetCore.Components.WebView.Maui.dll
Controls.Xaml -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Xaml.dll
Controls.Foldable -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Foldable.dll
Controls.Maps -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.HostApp/Debug/net10.0-android/Microsoft.Maui.Controls.Maps.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:06:35.95
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Broadcasting: Intent { act=android.intent.action.CLOSE_SYSTEM_DIALOGS flg=0x400000 }
Broadcast completed: result=0
Determining projects to restore...
All projects are up-to-date for restore.
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Graphics -> /home/vsts/work/1/s/artifacts/bin/Graphics/Debug/net10.0/Microsoft.Maui.Graphics.dll
Controls.CustomAttributes -> /home/vsts/work/1/s/artifacts/bin/Controls.CustomAttributes/Debug/net10.0/Controls.CustomAttributes.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Essentials -> /home/vsts/work/1/s/artifacts/bin/Essentials/Debug/net10.0/Microsoft.Maui.Essentials.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Core -> /home/vsts/work/1/s/artifacts/bin/Core/Debug/net10.0/Microsoft.Maui.dll
Controls.BindingSourceGen -> /home/vsts/work/1/s/artifacts/bin/Controls.BindingSourceGen/Debug/netstandard2.0/Microsoft.Maui.Controls.BindingSourceGen.dll
##vso[build.updatebuildnumber]10.0.60-ci+azdo.13717716
Controls.Core -> /home/vsts/work/1/s/artifacts/bin/Controls.Core/Debug/net10.0/Microsoft.Maui.Controls.dll
VisualTestUtils -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils/Debug/netstandard2.0/VisualTestUtils.dll
UITest.Core -> /home/vsts/work/1/s/artifacts/bin/UITest.Core/Debug/net10.0/UITest.Core.dll
VisualTestUtils.MagickNet -> /home/vsts/work/1/s/artifacts/bin/VisualTestUtils.MagickNet/Debug/netstandard2.0/VisualTestUtils.MagickNet.dll
UITest.NUnit -> /home/vsts/work/1/s/artifacts/bin/UITest.NUnit/Debug/net10.0/UITest.NUnit.dll
UITest.Appium -> /home/vsts/work/1/s/artifacts/bin/UITest.Appium/Debug/net10.0/UITest.Appium.dll
UITest.Analyzers -> /home/vsts/work/1/s/artifacts/bin/UITest.Analyzers/Debug/netstandard2.0/UITest.Analyzers.dll
Controls.TestCases.Android.Tests -> /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
Test run for /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll (.NETCoreApp,Version=v10.0)
VSTest version 18.0.1 (x64)
Starting test execution, please wait...
A total of 1 test files matched the specified pattern.
/home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.8.2+699d445a1a (64-bit .NET 10.0.0)
[xUnit.net 00:00:00.11] Discovering: Controls.TestCases.Android.Tests
[xUnit.net 00:00:00.46] Discovered: Controls.TestCases.Android.Tests
NUnit Adapter 4.5.0.0: Test execution started
Running selected tests in /home/vsts/work/1/s/artifacts/bin/Controls.TestCases.Android.Tests/Debug/net10.0/Controls.TestCases.Android.Tests.dll
NUnit3TestExecutor discovered 1 of 1 NUnit test cases using Current Discovery mode, Non-Explicit run
>>>>> 04/01/2026 22:47:39 FixtureSetup for Issue22197(Android)
>>>>> 04/01/2026 22:47:41 LineHeightWithHTMLLabelShouldWork Start
>>>>> 04/01/2026 22:47:45 LineHeightWithHTMLLabelShouldWork Stop
Passed LineHeightWithHTMLLabelShouldWork [3 s]
NUnit Adapter 4.5.0.0: Test execution complete
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 21.7490 Seconds
📁 Fix files reverted (3 files)
eng/pipelines/ci-copilot.ymlsrc/Controls/src/Core/Label/Label.Mapper.cssrc/Controls/src/Core/Label/Label.iOS.cs
New files (not reverted):
github-merge-flow-release-11.jsonc
🤖 AI Summary📊 Expand Full Review —
|
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #31202 | Change !IsPlainText(label) → label.HasFormattedTextSpans in 3 mapper methods; add LineHeight/TextDecorations/CharacterSpacing to iOS MapFormatting |
✅ PASSED (Gate) | Label.Mapper.cs, Label.iOS.cs |
Clean minimal fix |
🔧 Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | try-fix (claude-opus-4.6) | label.IsSet() guard — apply LineHeight/TextDecorations/CharacterSpacing to HTML labels only when property explicitly set, following MapFont/MapTextColor pattern |
✅ PASS | Label.Mapper.cs, Label.iOS.cs |
Conservative; extra complexity without clear benefit |
| 2 | try-fix (claude-sonnet-4.6) | Non-default value check — label.LineHeight != -1.0, label.TextDecorations != None, label.CharacterSpacing != 0.0 |
✅ PASS | Label.Mapper.cs, Label.iOS.cs |
Hardcoded magic default values; more fragile |
| 3 | try-fix (gpt-5.3-codex) | TextType-transition approach — trigger property refreshes from MapTextType when TextType==Html |
✅ PASS | Label.Mapper.cs |
Single-file fix; misses case where properties set before TextType |
| 4 | try-fix (gpt-5.4, gemini unavailable) | Rename IsPlainText() → ShouldSkipFormattingForSpans() with only HasFormattedTextSpans check |
✅ PASS | Label.Mapper.cs, Label.iOS.cs |
Functionally identical to PR's fix; just a rename |
| PR | PR #31202 | Change !IsPlainText(label) → label.HasFormattedTextSpans in 3 mapper methods; add LineHeight/TextDecorations/CharacterSpacing to iOS MapFormatting |
✅ PASSED (Gate) | Label.Mapper.cs, Label.iOS.cs |
Simplest direct fix; minimal changes |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| claude-opus-4.6 | 1 | No | NO NEW IDEAS |
| claude-sonnet-4.6 | 1 | No | NO NEW IDEAS |
| gpt-5.3-codex | 1 | Yes → No (R2) | Suggested HTML pipeline approach; withdrawn after analysis showed conflict with HTML/CSS |
| gpt-5.4 | 1 | Yes → No (R2) | Same HTML pipeline suggestion; withdrawn after analysis |
Exhausted: Yes
Selected Fix: PR's fix — Simplest and most direct. The !IsPlainText(label) → label.HasFormattedTextSpans change correctly identifies that the only reason to skip these properties is FormattedText spans, not HTML mode. Attempts 1-2 add unnecessary complexity. Attempt 3 is incomplete (misses initial render). Attempt 4 is functionally identical but adds a rename. PR fix is optimal.
📋 Report — Final Recommendation
✅ Final Recommendation: APPROVE
Phase Status
| Phase | Status | Notes |
|---|---|---|
| Pre-Flight | ✅ COMPLETE | 2 issues (#22193, #22197), Android+iOS platforms |
| Gate | ✅ PASSED | android — LineHeightWithHTMLLabelShouldWork passes with fix |
| Try-Fix | ✅ COMPLETE | 4 attempts, all 4 passing; cross-pollination exhausted in 2 rounds |
| Report | ✅ COMPLETE |
Summary
PR #31202 fixes LineHeight, TextDecorations, and CharacterSpacing not being applied to HTML labels (TextType="Html") on Android and iOS. The fix is minimal (6 lines changed across 2 implementation files) and surgically correct. Four independent fix attempts all passed, and cross-pollination across all models confirmed no better alternative exists. The PR's approach is the simplest of all candidates.
Root Cause
MapLineHeight, MapTextDecorations, and MapCharacterSpacing in Label.Mapper.cs were guarded by !IsPlainText(label). IsPlainText() returns false when either HasFormattedTextSpans is true OR TextType != TextType.Text — meaning HTML labels triggered the early return and never had these properties applied. The guard was semantically wrong: the intent was to skip span-based FormattedText, but HTML text got caught in the same exclusion. On iOS, MapFormatting also didn't refresh these properties when TextType changes to Html.
Fix Quality
Excellent. The change is:
- Correct: Replaces
!IsPlainText(label)withlabel.HasFormattedTextSpans, which is exactly the right predicate — only skip when FormattedText spans are present, not for HTML - Minimal: 3 one-line changes in the mapper, 3 added lines in the iOS formatter
- Consistent:
IsPlainText()itself is still used inLabel.iOS.cs(MapFormatting), so it remains without breaking existing callers - Well-tested: UITest screenshot comparison for both HTML and plain text labels side-by-side
Minor issues identified:
AutomationId="label"is too generic (noted in review comment from copilot-pull-request-reviewer) — consider"htmlTextTypeLabel"for uniqueness- Missing newline at end of 3 new test files (
Issue22197.xaml,Issue22197.xaml.cs,Issue22197.cs) — cosmetic only - Snapshots added for Android, Mac, Windows, and iOS-26 but not other iOS configurations — acceptable for screenshot tests
Try-Fix comparison: 4 independent attempts all passed but each was more complex than the PR's approach:
- Attempt 1 adds
IsSet()guards (over-engineering) - Attempt 2 hardcodes default values (fragile)
- Attempt 3 triggers from MapTextType (incomplete — misses initial render order)
- Attempt 4 is functionally identical with an unnecessary rename
The PR's simple 2-character guard replacement (!IsPlainText → HasFormattedTextSpans) is the optimal solution.
- Replace non-existent PR numbers (#34000, #33500, #33000, #34100) with real merged PRs (#34024, #34727, #31202, #28713, #34723) - Add "in dotnet/maui" to all prompts to prevent agent asking for repo - All PRs verified as real merged PRs with actual code changes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Description of Change
Corrects mapping logic for LineHeight, TextDecorations, and CharacterSpacing to apply to HTML labels. Updates iOS handler to refresh these properties when TextType changes. Adds test case for Issue22197 to verify correct rendering of HTML labels with these properties.
Issues Fixed
Fixes #22193
Fixes #22197