Skip to content

fix: HtmlView honours Style/Class instead of silently dropping them - #1288

Merged
rbuergi merged 3 commits into
mainfrom
fix/htmlview-honours-style
Aug 12, 2026
Merged

rbuergi merged 3 commits into
mainfrom
fix/htmlview-honours-style

Conversation

@rbuergi

@rbuergi rbuergi commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

The defect

HtmlView.razor's non-clickable branch emitted the bare fragment with no wrapper element:

@if (ViewModel.IsClickable)
{
    <div @onclick="OnClick" style="@Style" class="@Class">@Html</div>
}
else
{
    @Html          ← no wrapper element, no Style, no Class
}

Style and Class are public API on UiControl and are bound for every control by
BlazorView.BindData (BlazorView.razor.cs:494-496) — so an author could set them and this view
silently discarded them unless the control happened to be clickable. No error, no log, no fallback.
Controls.Html(x).WithStyle("width: 100%") did nothing.

Same defect class as #742 (ButtonView / IconView / BadgeView), which is why the fix and its tests
live alongside those in ControlStyleRenderingTest.

Driving case: raw <svg> emitted via Controls.Html(...) becomes a direct flex child of its
FluentStack and collapses toward its intrinsic size — the Reinsurance charts at ~6% of column width.
The obvious author fix was the exact instruction being dropped, which made it look unfixable rather
than merely broken. This PR is the platform fix only — the charts themselves are being moved onto
framework chart controls separately (Reinsurance PR #52); nothing here chases that symptom.

Wrapping strategy: conditional, gated on the declaration

The fragment is wrapped in a <div style class> only when the author declared Style or Class.
Otherwise the bare fragment is emitted exactly as today.

Why not unconditional — consumer evidence

HtmlControl is the one control with no element of its own: Data is emitted as a raw fragment, and
~416 call sites depend on that (334 in src/, 82 in samples/ + content/ + memex/). Four
concrete shapes in the tree break under an unconditional wrapper:

Call site Fragment What a wrapper does
src/MeshWeaver.Blazor/BlazorViewRegistry.cs:214 (FallbackHtml) bare HTML-encoded text, no element at all inline text run → block box, for every unrecognised control anywhere in the portal
samples/Northwind/.../LayoutTemplates.cs:43 (GrowthPercentage) <span style='color:green'>+12%</span> inline → block: a line break where there was none
ContentIndexSettingsTab.BuildChunkPanel three top-level siblings into a gap:6px stack three gapped flex children collapse into one, dropping the gaps
samples/Graph/Data/ACME/Project/Todo/Source/TodoLayoutAreas.cs:117 <h1 style="margin: 0; flex: 1; min-width: 200px;"> 🚨 the flex: 1 is on the fragment's own root precisely because that root IS the direct flex child of the enclosing FluentStack. A wrapper interposes a non-grow block and makes it inert

The last is decisive: a call site that provably depends on the bare-fragment behaviour today. It lives
in samples/Graph/Data/.../Source/ — in-mesh source, compiled at runtime, rendered by no build and
no test
— so that regression would surface only in a running portal.

Why gate on the declaration, not the resolved value

HasStyling reads ViewModel.Style/ViewModel.Class (the declaration) rather than the bound
Style/Class fields (the resolved values). Either may be a JsonPointerReference that resolves a
frame or more after the first render; gating on the resolved value would render the fragment bare and
then restructure the DOM around it when the pointer lands. The declaration is known at bind time and
never changes for a given view-model, so the box is stable from frame one. Nothing is lost: BindData
feeds those fields from exactly those two properties, so a resolved value can only be non-null when the
declaration was.

"Never silently ignored again"

With the conditional wrapper there is no case where honouring Style is impossible — a <div> can
always carry it — so there is nothing to log or throw about. The contract is made explicit in the XML
docs on HtmlControl (bare fragment by default; setting Style/Class opts into a box) and pinned by the
tests below.

⚠️ Behaviour change: exactly three call sites — and a scare that turned out to be nothing

Reviewer-relevant, because I got this wrong first and the correction is the second commit.

I initially claimed "not one call site sets Style or Class on an HtmlControl." Then I found ~100
Controls.H2(...).WithStyle("margin: 0") sites and assumed they were HtmlControls, because
Controls.Title is one. They are not, and a test I wrote to pin the supposed consequence failed
with Unable to cast LabelControl to HtmlControl:

Controls.Title(text, n) -> HtmlControl   // emits raw <hN> markup — this view renders it
Controls.H1..H6(data)   -> LabelControl  // Label(data).WithTypo(Hn) — Label.razor already applies Style

Label.razor applies Style="@Style" in both branches, so those ~100 heading sites never had this bug
and are untouched. HeadingFactories_AreLabelControls_NotHtmlControls now pins that distinction, so
re-implementing H1..H6 on top of Title fails loudly instead of silently widening the blast radius.

A balanced-paren scan (multi-line aware) for Controls.Html / Controls.Title / new HtmlControl
followed by a .WithStyle/.WithClass chain, across src/ samples/ content/ memex/, finds three
real call sites — the true blast radius:

Call site Style Effect
samples/Todo/.../TodoLayoutAreas.cs:1717 WithMarginBottom("8px") on a <textarea> fragment gains its 8px bottom margin
src/MeshWeaver.Graph/VersionLayoutArea.cs:153 margin: 0 0 16px 0 on Controls.Title(…, 2) gains 16px below the version-page title
src/MeshWeaver.Graph/VersionLayoutArea.cs:443 margin: 0 0 16px 0 on Controls.Title(…, 2) as above

All three were asking for a style that was being dropped; all three now get it. For the two Title
sites the style lands on the wrapper, so the declared margin adds to the <h2>'s own UA margin rather
than replacing it — strictly better than dropping it, but the author's literal intent needs Title to
have its own control/view instead of borrowing HtmlControl. Left as a follow-up: it changes a
public factory's return type, including for in-mesh callers.

Tests — verified by falsification

Seven tests added to ControlStyleRenderingTest, which renders through the real HtmlRenderer
against a real monolith mesh and asserts on emitted HTML (asserting control.Style proves nothing —
the value was always set correctly; the markup dropped it).

I reverted HtmlView.razor to the pre-fix markup and re-ran the first four:

Failed  HtmlControl_ClassOnly_ReachesTheDom
Failed  HtmlControl_StyleAndClass_ReachTheDom
Failed! - Failed: 2, Passed: 2, Total: 4
  • The two positive tests fail without the fix → they genuinely pin the bug.
  • The two backwards-compatibility guards pass with and without the fix → they assert today's
    markup is unchanged, so they are real guards, not tautologies:
    • HtmlControl_WithoutStyleOrClass_EmitsTheBareFragment — asserts html.Should().Be(fragment)
      exactly, for <span>inline</span>. This is the backwards-compatibility assertion: an unstyled
      HtmlControl emits markup byte-identical to today, no wrapper introduced.
    • HtmlControl_MultiRootFragment_StaysMultiRoot — <div>label</div><div>value</div> stays two
      siblings, so a parent stack's gaps are not collapsed.

Plus TitleControl_IsAnHtmlControl_SoItsStyleNowReachesAWrapper,
TitleControl_WithoutStyle_IsStillABareHeading, and
HeadingFactories_AreLabelControls_NotHtmlControls.

With the fix: Passed! - Failed: 0, Passed: 13, Total: 13.

Verification

Check Result
dotnet build src/MeshWeaver.Layout -c Release -warnaserror 0 Warning(s), 0 Error(s)
dotnet build src/MeshWeaver.Blazor -c Release -warnaserror 0 Warning(s), 0 Error(s)
dotnet build test/MeshWeaver.Hosting.Blazor.Test -c Release -warnaserror 0 Warning(s), 0 Error(s)
ControlStyleRenderingTest (13) Passed
DocumentationLinkIntegrityTest (What's New note added) Passed

What's New entry added (Category: Fix).


📋 Audit: Style / Class across all Blazor control views — information only, NOT fixed here

Requested alongside the fix so the owner can decide whether the third bucket warrants a follow-up.
Nothing below is changed by this PR.

72 control views examined — every .razor under src/MeshWeaver.Blazor/Components/ (incl.
Monaco/) whose @inherits resolves to BlazorView<,> or a subclass; code-behinds checked for all.

Base-class facts: BlazorView.BindData (BlazorView.razor.cs:485-497) binds Id/Class/Style for
every control view. SkinnedView.cs:40-46 repeats the same three binds. FormComponentBase.cs:60-70
folds Style+Width+Height into ComputedStyle, which form views are supposed to emit instead of Style.

🔴 Bucket 3 — silently drops it (25)

Drops both Style and Class (17)

View Evidence
AppearanceView root <div style="max-width: 500px;"> (l.6) — literal; @Style/@Class never appear
CatalogView style="@ContainerStyle" (l.5) where ContainerStyle is a literal in the code-behind (l.27)
CollaborativeMarkdownView root class="collab-md-container @ViewModeClass" (l.11) — private computed class, not Class
CommentableView root class="commentable-wrapper" (l.7); renders ViewModel.Areas children, so nothing downstream carries the container's own Style either
DialogView style="@GetDialogStyle()" (l.9) composes only --dialog-width/--dialog-height from Size
ExportDocumentView root class="export-document-view" style="max-width: 720px;" (l.16) — literal
LayoutAreaDefinitionView root <a class="card layout-area-card …"> (l.4) — fixed classes, no style attribute
MarkdownEditorView root class="collaborative-editor-container" (l.25); no @Style/@Class in file
MeshNodeCardView literal style="text-decoration: none; …" (l.12, 18) + <FluentCard Class="mesh-node-card" Style="cursor: pointer; …"> (l.60)
MeshNodeContentEditorView root class="mesh-node-content-editor" style="display: flex; …" (l.16) — literal
MeshNodePickerView ⚠️ inherits FormComponentBase yet root is <div class="mesh-node-picker…"> (l.6) with no ComputedStyle — drops Style, Class, Width and Height
MeshNodeRoleEditorView branch roots literal style="display: flex; align-items: center; gap: 8px;" (l.15)
MeshNodeThumbnailView <FluentCard Class="mesh-node-thumbnail" Style="min-width: 320px; …"> (l.4) — both params present but hard-coded, shadowing the bound values
NavLink class="menu-item-link@(IsActive…)" (l.6) / Class="@(IsActive ? "active" : null)" (l.21) — Class overwritten by active-state token; Style never appears
NodeExportView root class="node-export-view" style="max-width: 800px;" (l.11) — literal
NodeImportView root class="node-import-view" style="max-width: 800px;" (l.9) — literal
Monaco/DiffEditorView root style="height: @(ViewModel?.Height ?? "500px"); width: 100%;" (l.8) reads ViewModel.Height directly; CssClass fixed

Drops Class only (Style works) (8)

View Evidence
EditorView <div style="@Style"> (l.4); inner class="form-group" fixed; @Class never emitted
Label both branches set Style="@Style" (l.13, 26) on FluentLabel; Class never passed though the param exists
LayoutAreaView class="layout-area-container @(IsContentLoaded…) …" style="@Style" (l.5) — @Class not merged
MarkdownView <article class="markdown-body" … style="…; @Style"> (l.3) — class list fixed
MeshNodeCollectionView <div class="mesh-node-collection" style="@Style"> (l.8)
NumberFieldView @ComputedStyle applied correctly (l.8), but inner input uses class="@appearanceClass" (l.60), a local value
TabsView <FluentTabs … Style="@Style"> (l.4-10); FluentTabs accepts Class, not passed
ThreadMessageBubbleView class="thread-msg-bubble @(IsUser…)" style="@Style" (l.6) — @Class not merged

🟡 Bucket 4 — unclear (1)

View Why
NamedAreaView renders real markup in the not-yet-loaded branch (class="skeleton-placeholder" l.37, FluentStack with conditional literal Style l.50) but in the loaded branch emits only <DispatchView ViewModel="@RootControl"> (l.72) — a different control applying its own Style. The NamedArea's own Style/Class are never rendered, but whether they are meant to (there is no steady-state element to carry them) is a design call I could not settle from the code.

🟢 Bucket 1 — applies Style and Class (39)

BadgeView, BodyContentView, ButtonView, CardView, Checkbox, Combobox, ComparisonBarsView,
DataGridView, DateTimeView, DocumentSourceView, FooterView, HeaderView, HighlightView,
HtmlView (this PR — conditional wrapper), IconView, ItemTemplate, KpiStripView,
LayoutGridItemView, LayoutGridView, LayoutStackView, LayoutView, Listbox, MainView,
MenuItemView
, NavGroup, NavMenuView, ProgressView, RadioGroupView, SearchView,
SelectView*, SplitterPane, SplitterView, Switch, TabView, TextAreaView, TextFieldView,
ToolbarView, TowerView, VideoView.

Mechanism is Class="@Class" + Style="@Style" on a Fluent component, or class="… @Class" style="@Style" on a plain element; the form inputs (Checkbox, Combobox, Listbox,
RadioGroupView, SearchView, Switch, TextFieldView, NumberFieldView) correctly use
ComputedStyle.

* Three caveats worth a look, on a different axis (Style/Class are fine):

  • SelectView and DateTimeView are FormComponentBase views that pass Style instead of
    ComputedStyle → the bound Width/Height are dropped.
  • MenuItemView re-binds DataBind(Skin.Style, x => x.Style) (l.127) after the base bound
    ViewModel.Style, so the skin wins and a null skin style nulls the field. Coherent with
    MenuItemControl.WithStyle(object) routing into the skin, but the inherited
    WithStyle(Func<StyleBuilder,…>) overload writes the control's Style and is clobbered.

⚪ Bucket 2 — deliberately N/A (7)

View Reason
SpacerView FluentSpacer exposes no Style/Class parameter (the case the existing test doc-comment already calls out)
ContainerView emits no element of its own — a foreach over children rendering one DispatchView each (l.3-11). ⚠️ Consequence worth noting: a container control's own Style/Class therefore have no effect anywhere
DispatchView pure dispatcher — the dispatched concrete view applies Style/Class
EditFormView re-dispatches the same ViewModel (l.17) after the skin was popped; the terminal view applies it
PropertyView <dt>/<dd> chrome plus <DispatchView ViewModel="@ViewModel"> (l.13) — same control, terminal view applies it
RedirectView renders no markup; OnInitializedAsync navigates
SlideShowView renders no visible chrome by design — registers a JS keydown listener only

⚠️ Outside the four buckets — never bound at all (3)

SearchBoxView, MeshSearchView and Monaco/CodeEditorView are registered as control views in
BlazorViewRegistry.cs (l.144, 147, 134) but derive from plain ComponentBase and take
[Parameter] ViewModel/Stream/Area by hand. Style/Class are therefore not merely dropped in
markup — they are never bound. Strictly outside the bucket definitions, but the same user-visible
defect.

Counts

Bucket Count
🔴 Silently drops (17 both + 8 Class-only) 25
🟡 Unclear 1
🟢 Applies both 39
⚪ Deliberately N/A 7
Total control views examined 72
⚠️ Registered as views but never bind Style/Class 3 (separate)

Whether bucket 3 warrants a systemic follow-up is the owner's call — several of the 17 are
single-purpose views whose box is genuinely fixed by design, but MeshNodePickerView (drops
Width/Height too) and the 8 Class-only drops look like plain omissions.


🤖 Generated with Claude Code

HtmlView's non-clickable branch emitted the bare fragment with no wrapper,
so Style and Class — bound by BlazorView.BindData for every control and
public API on UiControl — were discarded unless the control happened to be
clickable. Controls.Html(x).WithStyle("width: 100%") did nothing at all: no
error, no log, no fallback. Same defect class as #742 (ButtonView/IconView/
BadgeView), which is why the fix and its tests live alongside those.

Live consequence: raw <svg> emitted via Controls.Html becomes a direct flex
child of its FluentStack and collapses to its intrinsic size; the obvious
author fix was the exact instruction being dropped.

The wrapper is CONDITIONAL on the author having declared Style or Class.
HtmlControl has no element of its own and ~400 call sites depend on that:
BlazorViewRegistry.FallbackHtml emits bare encoded text for every
unrecognised control, Northwind's GrowthPercentage emits an inline <span>,
the settings tabs emit several top-level siblings meant to be separate
gapped children of their stack, and Todo/Source/TodoLayoutAreas writes
flex: 1 onto the fragment's own root precisely because that root is the
direct flex child. Wrapping unconditionally would turn inline runs into
block boxes, collapse gapped siblings into one child and make that flex: 1
inert — and most of those call sites are in-mesh source that no build and
no test renders, so it would surface only in a running portal. Not one of
them sets Style or Class, so gating on the declaration leaves every one
byte-identical.

Gates on ViewModel.Style/Class (the declaration) rather than the bound
fields (the resolved values): either may be a JsonPointerReference that
lands after the first render, and gating on the resolved value would
restructure the DOM mid-flight. A resolved value can only be non-null when
the declaration was, so nothing is lost.

Tests, verified by falsification against the pre-fix markup: the two
positive tests fail without the fix, the two bare-fragment
backwards-compatibility guards pass with and without it.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Copilot AI lite review requested due to automatic review settings August 12, 2026 12:05

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

Fixes a rendering hole in the Blazor HtmlView so HtmlControl no longer silently drops author-provided Style/Class in the non-clickable branch, while preserving the existing “bare fragment” behavior when no styling was declared.

Changes:

  • Update HtmlView.razor to conditionally wrap the fragment in a <div> only when Style and/or Class was declared, ensuring those attributes reach the DOM without breaking existing multi-root/inline fragment layouts.
  • Add renderer-level tests in ControlStyleRenderingTest to prove the bug (style/class reach DOM) and guard backwards-compatibility (unstyled fragments remain bare, multi-root stays multi-root).
  • Document the “bare fragment by default; wrapper when styling is declared” contract for HtmlControl, and add a What’s New entry describing the user-visible fix.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.

File Description
src/MeshWeaver.Blazor/Components/HtmlView.razor Adds a HasStyling-gated wrapper so Style/Class are honored for non-clickable Html controls without changing existing unstyled fragment rendering.
test/MeshWeaver.Hosting.Blazor.Test/ControlStyleRenderingTest.cs Adds positive + backwards-compatibility tests using the real HtmlRenderer to pin the regression and prevent unconditional wrapping.
src/MeshWeaver.Layout/HtmlControl.cs Updates XML docs to clarify how/when HtmlControl renders as a bare fragment vs a wrapped box to carry styling.
src/MeshWeaver.Documentation/Data/WhatsNew/2026-08-13-content-blocks-no-longer-ignore-the-width-you-set.md Adds a What’s New note describing the fix and its user impact.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/MeshWeaver.Layout/HtmlControl.cs Resolved
@github-actions

github-actions Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 0)

707 tests  ±0   707 ✅ ±0   14m 11s ⏱️ +34s
 10 suites ±0     0 💤 ±0 
 10 files   ±0     0 ❌ ±0 

Results for commit ba87688. ± Comparison against base commit dba5eb8.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 3)

   11 files  ±0     11 suites  ±0   8m 1s ⏱️ -33s
2 140 tests +3  2 037 ✅ +3  103 💤 ±0  0 ❌ ±0 
2 389 runs  +8  2 286 ✅ +8  103 💤 ±0  0 ❌ ±0 

Results for commit ba87688. ± Comparison against base commit dba5eb8.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 1)

1 355 tests  +65   1 355 ✅ +68   7m 26s ⏱️ +53s
   11 suites ± 0       0 💤 ± 0 
   11 files   ± 0       0 ❌  -  3 

Results for commit ba87688. ± Comparison against base commit dba5eb8.

This pull request removes 244 and adds 309 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.CompileLeafStabilityTest ‑ CompileAndGetConfigurations_FreshNodeTypes_AllEmitWithinBound
MeshWeaver.Hosting.Monolith.Test.CompileLeafStabilityTest ‑ Kickoff_FreshNodeTypes_AllSettle_NoStall
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ ConcurrentRequests_OnFreshNodeType_BothSucceedConsistently
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ FreshCompile_TerminalStateIsComplete
MeshWeaver.Hosting.Monolith.Test.CompileSourceSnapshotWedgeTest ‑ HungSourceSnapshot_SettlesTerminalError_ThenFreshTriggerRecovers
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ ActivationEcho_MustNotDropUnknownContentMembers
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ CreateThenUpdate_PersistsWithoutRelyingOnActivationEcho
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ EditThroughNarrowerType_PersistsEditAndKeepsUnknownMembers
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ HubOptions_RoundTrip_PreservesUnknownContentMembers
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ PureActivation_DoesNotWriteStorageAtAll
…
MeshWeaver.Hosting.Monolith.Test.CompileIncludeSystemScopeTest ‑ CodeInclude_ResolvesOnCompilePath_WhenNoAmbientIdentity
MeshWeaver.Hosting.Monolith.Test.CompileLegBoundWedgeTest ‑ HungAssemblyStoreUpload_StillSettlesOk_AndNamesTheLegOnTheActivityLog
MeshWeaver.Hosting.Monolith.Test.CompileLegBoundWedgeTest ‑ HungRoslynLeg_SettlesTerminalError_NamingTheLeg_ThenRecovers
MeshWeaver.Hosting.Monolith.Test.CompileSourceSnapshotReuseTest ‑ Compile_TakesOneSourceSnapshot_AndEveryStageReusesIt
MeshWeaver.Hosting.Monolith.Test.ConsoleCaptureExecutionContextLeakTest ‑ CaptureScopeDisposal_SeversTargetFromCapturedExecutionContexts
MeshWeaver.Hosting.Monolith.Test.CopyModifyCopyBackTest ‑ CopyModifyCopyBack_UpdatesOnlyDeltas
MeshWeaver.Hosting.Monolith.Test.CreatableTypesIntegrationTest ‑ ACME_CreatableTypes_IncludesProjectAndGlobalTypes
MeshWeaver.Hosting.Monolith.Test.CreatableTypesIntegrationTest ‑ CompleteCreateFlow_GetTypes_Create_Edit_Succeeds
MeshWeaver.Hosting.Monolith.Test.CreatableTypesIntegrationTest ‑ CreatableTypes_SortedByOrder
MeshWeaver.Hosting.Monolith.Test.CreatableTypesIntegrationTest ‑ CreatableTypes_WithExplicitConfig_OverridesAuto
…

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 5)

1 824 tests  +21   1 632 ✅ +21   8m 57s ⏱️ +27s
   11 suites ± 0     192 💤 ± 0 
   11 files   ± 0       0 ❌ ± 0 

Results for commit ba87688. ± Comparison against base commit dba5eb8.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 4)

1 320 tests  +7   1 310 ✅ +7   12m 15s ⏱️ -19s
   11 suites ±0      10 💤 ±0 
   11 files   ±0       0 ❌ ±0 

Results for commit ba87688. ± Comparison against base commit dba5eb8.

♻️ This comment has been updated with latest results.

@github-actions

github-actions Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results (shard 2)

2 114 tests   - 64   2 113 ✅  - 64   6m 24s ⏱️ - 2m 8s
   11 suites ± 0       1 💤 ± 0 
   11 files   ± 0       0 ❌ ± 0 

Results for commit ba87688. ± Comparison against base commit dba5eb8.

This pull request removes 308 and adds 244 tests. Note that renamed tests count towards both.
MeshWeaver.Hosting.Monolith.Test.CompileLegBoundWedgeTest ‑ HungAssemblyStoreUpload_StillSettlesOk_AndNamesTheLegOnTheActivityLog
MeshWeaver.Hosting.Monolith.Test.CompileLegBoundWedgeTest ‑ HungRoslynLeg_SettlesTerminalError_NamingTheLeg_ThenRecovers
MeshWeaver.Hosting.Monolith.Test.CompileSourceSnapshotReuseTest ‑ Compile_TakesOneSourceSnapshot_AndEveryStageReusesIt
MeshWeaver.Hosting.Monolith.Test.ConsoleCaptureExecutionContextLeakTest ‑ CaptureScopeDisposal_SeversTargetFromCapturedExecutionContexts
MeshWeaver.Hosting.Monolith.Test.CopyModifyCopyBackTest ‑ CopyModifyCopyBack_UpdatesOnlyDeltas
MeshWeaver.Hosting.Monolith.Test.CreatableTypesIntegrationTest ‑ ACME_CreatableTypes_IncludesProjectAndGlobalTypes
MeshWeaver.Hosting.Monolith.Test.CreatableTypesIntegrationTest ‑ CompleteCreateFlow_GetTypes_Create_Edit_Succeeds
MeshWeaver.Hosting.Monolith.Test.CreatableTypesIntegrationTest ‑ CreatableTypes_SortedByOrder
MeshWeaver.Hosting.Monolith.Test.CreatableTypesIntegrationTest ‑ CreatableTypes_WithExplicitConfig_OverridesAuto
MeshWeaver.Hosting.Monolith.Test.CreatableTypesIntegrationTest ‑ CreateForm_DefaultType_ShouldBeParentNodeType
…
MeshWeaver.Hosting.Monolith.Test.CompileLeafStabilityTest ‑ CompileAndGetConfigurations_FreshNodeTypes_AllEmitWithinBound
MeshWeaver.Hosting.Monolith.Test.CompileLeafStabilityTest ‑ Kickoff_FreshNodeTypes_AllSettle_NoStall
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ ConcurrentRequests_OnFreshNodeType_BothSucceedConsistently
MeshWeaver.Hosting.Monolith.Test.CompileSingleDriverConsistencyTest ‑ FreshCompile_TerminalStateIsComplete
MeshWeaver.Hosting.Monolith.Test.CompileSourceSnapshotWedgeTest ‑ HungSourceSnapshot_SettlesTerminalError_ThenFreshTriggerRecovers
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ ActivationEcho_MustNotDropUnknownContentMembers
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ CreateThenUpdate_PersistsWithoutRelyingOnActivationEcho
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ EditThroughNarrowerType_PersistsEditAndKeepsUnknownMembers
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ HubOptions_RoundTrip_PreservesUnknownContentMembers
MeshWeaver.Hosting.Monolith.Test.ContentNarrowingPersistenceTest ‑ PureActivation_DoesNotWriteStorageAtAll
…

♻️ This comment has been updated with latest results.

…trol

Self-review caught a false claim in the previous commit's reasoning. I had
asserted "not one call site sets Style or Class on an HtmlControl", then
found ~100 Controls.H2(...).WithStyle("margin: 0") sites and assumed they
were HtmlControls because Controls.Title is one. They are not:

  Controls.Title(text, n) -> HtmlControl   (emits raw <hN> markup)
  Controls.H1..H6(data)   -> LabelControl  (Label(data).WithTypo(Hn))

H1..H6 render through Label.razor, which already applies Style="@Style" in
both branches, so those ~100 sites never had this bug and are untouched.
The test that exposed this failed with "Unable to cast LabelControl to
HtmlControl".

A balanced-paren scan for Controls.Html / Controls.Title / new HtmlControl
followed by a .WithStyle/.WithClass chain, across src/ samples/ content/
memex/, finds THREE real call sites:

  samples/Todo/.../TodoLayoutAreas.cs:1717  Controls.Html(<textarea…>)
  src/MeshWeaver.Graph/VersionLayoutArea.cs:153, 443  Controls.Title(…, 2)

All three were asking for a style that was being dropped and now get it.

Replaces the inaccurate doc paragraph, and adds three tests: the two
Controls.Title cases (styled -> wrapper, unstyled -> bare <h2>), plus
HeadingFactories_AreLabelControls_NotHtmlControls, which pins the
Title/H1..H6 distinction so re-implementing the heading factories on top of
Title fails loudly instead of silently widening the blast radius.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 12, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

   65 files  ± 0     65 suites  ±0   57m 16s ⏱️ - 1m 6s
9 460 tests +32  9 154 ✅ +35  306 💤 ±0  0 ❌  - 3 
9 709 runs  +37  9 403 ✅ +40  306 💤 ±0  0 ❌  - 3 

Results for commit ba87688. ± Comparison against base commit dba5eb8.

♻️ This comment has been updated with latest results.

…lass

Copilot review: the remarks said HtmlControl "has no element of its own"
without noting that the clickable branch has always wrapped the fragment to
carry the click handler. Read literally, a consumer could conclude the
control never introduces an element.

Restates it as a default rather than an invariant: the bare fragment is what
you get when the control is non-clickable AND has no Style/Class; a click
action or a declared Style/Class each introduce exactly one <div>.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants