@@ -22,7 +22,7 @@
Code:
-<Literal Mode="LiteralMode.PassThrough" Text="<b>Literal</b>"></Literal>
+<Literal Mode="LiteralMode.PassThrough" Text="This is <b>literal</b> content."></Literal>
-<Literal Text="<b>Literal</b>"></Literal>
+<Literal Mode="LiteralMode.Encode" Text="This is <b>encoded</b> content."></Literal>
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginNameSample.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginNameSample.razor
index 1cefbf049..0bd6f8589 100644
--- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginNameSample.razor
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginNameSample.razor
@@ -5,8 +5,10 @@
@using BlazorWebFormsComponents.LoginControls;
+
+
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginSample.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginSample.razor
index 1ce30e62c..bb93a9987 100644
--- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginSample.razor
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginSample.razor
@@ -5,6 +5,7 @@
@using BlazorWebFormsComponents.LoginControls;
+
+
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginStatusAuthenticated.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginStatusAuthenticated.razor
index a34eaddb9..8cc1ae904 100644
--- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginStatusAuthenticated.razor
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginStatusAuthenticated.razor
@@ -6,9 +6,11 @@
LoginStatus Authenticated
+
+
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginStatusNotAuthenticated.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginStatusNotAuthenticated.razor
index 904b355bd..fe0b28e14 100644
--- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginStatusNotAuthenticated.razor
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginControls/LoginStatusNotAuthenticated.razor
@@ -5,8 +5,10 @@
LoginStatus Not Authenticated
+
+
@code
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginView/Index.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginView/Index.razor
new file mode 100644
index 000000000..3994336cf
--- /dev/null
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/LoginView/Index.razor
@@ -0,0 +1,46 @@
+@page "/ControlSamples/LoginView"
+@using BlazorWebFormsComponents.LoginControls
+
+
LoginView Component
+
+
+
+
+ The LoginView component displays different content depending on whether
+ the user is authenticated. Use AnonymousTemplate and LoggedInTemplate
+ to define the content for each state.
+
+
+
LoginView with Templates
+
+
+
+ You are not logged in. Please sign in to access member content.
+
+
+ Welcome back! You are logged in.
+
+
+
+
+@code {
+ protected override void OnInitialized()
+ {
+ StaticAuthStateProvider.Logout();
+ base.OnInitialized();
+ }
+}
+
+
+
+
Code:
+
+<LoginView>
+ <AnonymousTemplate>
+ <p>You are not logged in.</p>
+ </AnonymousTemplate>
+ <LoggedInTemplate>
+ <p>Welcome back!</p>
+ </LoggedInTemplate>
+</LoginView>
+
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/MultiView/Index.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/MultiView/Index.razor
index 94468a986..b95b668a1 100644
--- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/MultiView/Index.razor
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/MultiView/Index.razor
@@ -12,6 +12,7 @@
Basic MultiView
Current View: @(activeIndex + 1) of 3
+
@code {
private int activeIndex = 0;
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Panel/Index.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Panel/Index.razor
index 466635e97..a32c73971 100644
--- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Panel/Index.razor
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Panel/Index.razor
@@ -3,51 +3,48 @@
Panel Component Samples
-
Basic Panel
+
Basic Panel with GroupingText
-
- This content is inside a basic Panel.
- A Panel renders as a div element by default.
+
+
+
-
<Panel>
- <p>This content is inside a basic Panel.</p>
+<Panel GroupingText="User Info">
+ <Label Text="Name:" />
+ <TextBox />
</Panel>
-Panel with GroupingText (Fieldset)
+Panel with ScrollBars
-
- Name: John Doe
- Email: john@example.com
+
+ First paragraph of content inside the scrollable panel.
+ Second paragraph of content inside the scrollable panel.
+ Third paragraph of content inside the scrollable panel.
+ Fourth paragraph of content inside the scrollable panel.
-<Panel GroupingText="User Information">
- <p>Name: John Doe</p>
- <p>Email: john@example.com</p>
+<Panel ScrollBars="ScrollBars.Auto" Height="Unit.Pixel(100)">
+ <p>First paragraph of content inside the scrollable panel.</p>
+ <p>Second paragraph of content inside the scrollable panel.</p>
+ <p>Third paragraph of content inside the scrollable panel.</p>
+ <p>Fourth paragraph of content inside the scrollable panel.</p>
</Panel>
-Panel with Styling
+Panel with DefaultButton
-
- This panel has custom colors and border.
+
+
+
-<Panel BackColor="WebColor.LightYellow"
- ForeColor="WebColor.Navy"
- BorderColor="WebColor.Blue"
- BorderWidth="Unit.Pixel(2)"
- BorderStyle="BorderStyle.Solid"
- Width="Unit.Pixel(300)">
- <p>This panel has custom colors and border.</p>
+<Panel DefaultButton="btnSubmit">
+ <TextBox />
+ <Button ID="btnSubmit" Text="Submit" />
</Panel>
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/PasswordRecovery/Index.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/PasswordRecovery/Index.razor
index b14886b91..c791c6ae9 100644
--- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/PasswordRecovery/Index.razor
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/PasswordRecovery/Index.razor
@@ -17,10 +17,12 @@
Handle OnVerifyingUser to validate the username and set the security question
via the SetQuestion method. Handle OnVerifyingAnswer to verify the answer.
+
@_statusMessage
@@ -56,6 +58,7 @@
Custom Text Properties
Customize labels, titles, and messages for each step using the text properties.
+
Code:
<PasswordRecovery ID="PasswordRecovery2"
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/PlaceHolder/Index.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/PlaceHolder/Index.razor
index efb3fbda6..20eaf1492 100644
--- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/PlaceHolder/Index.razor
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/PlaceHolder/Index.razor
@@ -9,8 +9,8 @@
Basic PlaceHolder
- This content is inside a PlaceHolder.
- Note: No extra wrapper element is rendered around this content.
+ This content was added programmatically.
+ PlaceHolder renders no HTML of its own.
<PlaceHolder>
diff --git a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Table/Index.razor b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Table/Index.razor
index 8c6ff9ddb..56a3321f2 100644
--- a/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Table/Index.razor
+++ b/samples/AfterBlazorServerSide/Components/Pages/ControlSamples/Table/Index.razor
@@ -64,6 +64,7 @@
Table with Grid Lines
+
Name
@@ -81,6 +82,7 @@
Los Angeles
+
<Table GridLines="GridLines.Both" CellPadding="5">
...
</Table>
diff --git a/samples/AfterBlazorServerSide/wwwroot/Content/Images/banner.png b/samples/AfterBlazorServerSide/wwwroot/Content/Images/banner.png
new file mode 100644
index 000000000..b3e42ca8e
Binary files /dev/null and b/samples/AfterBlazorServerSide/wwwroot/Content/Images/banner.png differ
diff --git a/src/BlazorWebFormsComponents.Test/BulletedList/BulletStyle.razor b/src/BlazorWebFormsComponents.Test/BulletedList/BulletStyle.razor
index b04993868..2ec1c9081 100644
--- a/src/BlazorWebFormsComponents.Test/BulletedList/BulletStyle.razor
+++ b/src/BlazorWebFormsComponents.Test/BulletedList/BulletStyle.razor
@@ -91,7 +91,7 @@
// Assert
var ol = cut.Find("ol");
ol.ShouldNotBeNull();
- ol.GetAttribute("type").ShouldBe("1");
+ ol.GetAttribute("style").ShouldContain("list-style-type: decimal");
}
[Fact]
@@ -109,7 +109,7 @@
// Assert
var ol = cut.Find("ol");
ol.ShouldNotBeNull();
- ol.GetAttribute("type").ShouldBe("a");
+ ol.GetAttribute("style").ShouldContain("list-style-type: lower-alpha");
}
[Fact]
@@ -126,7 +126,8 @@
// Assert
var ol = cut.Find("ol");
- ol.GetAttribute("type").ShouldBe("A");
+ ol.ShouldNotBeNull();
+ ol.GetAttribute("style").ShouldContain("list-style-type: upper-alpha");
}
[Fact]
@@ -143,7 +144,8 @@
// Assert
var ol = cut.Find("ol");
- ol.GetAttribute("type").ShouldBe("i");
+ ol.ShouldNotBeNull();
+ ol.GetAttribute("style").ShouldContain("list-style-type: lower-roman");
}
[Fact]
@@ -160,7 +162,8 @@
// Assert
var ol = cut.Find("ol");
- ol.GetAttribute("type").ShouldBe("I");
+ ol.ShouldNotBeNull();
+ ol.GetAttribute("style").ShouldContain("list-style-type: upper-roman");
}
[Fact]
@@ -199,4 +202,38 @@
ol.GetAttribute("start").ShouldBe("5");
}
+ [Fact]
+ public void BulletedList_DefaultFirstBulletNumber_NoStartAttribute()
+ {
+ // Arrange
+ var items = new ListItemCollection
+ {
+ new ListItem("Item 1", "1")
+ };
+
+ // Act
+ var cut = Render(@ );
+
+ // Assert
+ var ol = cut.Find("ol");
+ ol.HasAttribute("start").ShouldBeFalse();
+ }
+
+ [Fact]
+ public void BulletedList_NumberedStyle_NoTypeAttribute()
+ {
+ // Arrange — WebForms uses CSS list-style-type, not the HTML type attribute
+ var items = new ListItemCollection
+ {
+ new ListItem("Item 1", "1")
+ };
+
+ // Act
+ var cut = Render(@ );
+
+ // Assert
+ var ol = cut.Find("ol");
+ ol.HasAttribute("type").ShouldBeFalse();
+ }
+
}
diff --git a/src/BlazorWebFormsComponents.Test/CheckBox/NoSpanWrapper.razor b/src/BlazorWebFormsComponents.Test/CheckBox/NoSpanWrapper.razor
new file mode 100644
index 000000000..30e110c56
--- /dev/null
+++ b/src/BlazorWebFormsComponents.Test/CheckBox/NoSpanWrapper.razor
@@ -0,0 +1,27 @@
+@inherits BlazorWebFormsTestContext
+@using Shouldly
+
+@code {
+
+ [Fact]
+ public void CheckBox_WithText_NoSpanWrapper()
+ {
+ // WebForms CheckBox renders and directly, no wrapper
+ var cut = Render(@ );
+
+ Should.Throw(() => cut.Find("span"));
+ cut.Find("input[type='checkbox']").ShouldNotBeNull();
+ cut.Find("label").ShouldNotBeNull();
+ }
+
+ [Fact]
+ public void CheckBox_WithoutText_NoSpanWrapper()
+ {
+ // WebForms CheckBox without text renders just , no wrapper
+ var cut = Render(@ );
+
+ Should.Throw(() => cut.Find("span"));
+ cut.Find("input[type='checkbox']").ShouldNotBeNull();
+ }
+
+}
diff --git a/src/BlazorWebFormsComponents.Test/FileUpload/IdRendering.razor b/src/BlazorWebFormsComponents.Test/FileUpload/IdRendering.razor
new file mode 100644
index 000000000..09e01aa35
--- /dev/null
+++ b/src/BlazorWebFormsComponents.Test/FileUpload/IdRendering.razor
@@ -0,0 +1,26 @@
+@inherits BlazorWebFormsTestContext
+@using Shouldly
+
+@code {
+
+ [Fact]
+ public void FileUpload_WithID_RendersExactId()
+ {
+ // WebForms renders the developer-set ID with no GUID suffix
+ var cut = Render(@ );
+
+ var input = cut.Find("input[type='file']");
+ input.GetAttribute("id").ShouldBe("myUpload");
+ }
+
+ [Fact]
+ public void FileUpload_WithoutID_NoIdAttribute()
+ {
+ // WebForms does not render an id when none is set
+ var cut = Render(@ );
+
+ var input = cut.Find("input[type='file']");
+ input.HasAttribute("id").ShouldBeFalse();
+ }
+
+}
diff --git a/src/BlazorWebFormsComponents.Test/GridView/AccessibleHeaderDefault.razor b/src/BlazorWebFormsComponents.Test/GridView/AccessibleHeaderDefault.razor
new file mode 100644
index 000000000..41b3345cf
--- /dev/null
+++ b/src/BlazorWebFormsComponents.Test/GridView/AccessibleHeaderDefault.razor
@@ -0,0 +1,60 @@
+@inherits BlazorWebFormsTestContext
+@using Shouldly
+
+@code {
+
+ [Fact]
+ public void GridView_DefaultUseAccessibleHeader_RendersThWithScope()
+ {
+ // WebForms defaults UseAccessibleHeader to true, rendering
+ var data = GetTestItems();
+
+ var cut = Render(@
+
+
+
+ );
+
+ var headers = cut.FindAll("th");
+ headers.Count.ShouldBeGreaterThan(0);
+ foreach (var header in headers)
+ {
+ header.GetAttribute("scope").ShouldBe("col");
+ }
+ }
+
+ [Fact]
+ public void GridView_UseAccessibleHeaderFalse_NoScope()
+ {
+ var data = GetTestItems();
+
+ var cut = Render(@
+
+
+
+ );
+
+ var headers = cut.FindAll("th");
+ headers.Count.ShouldBeGreaterThan(0);
+ foreach (var header in headers)
+ {
+ header.HasAttribute("scope").ShouldBeFalse();
+ }
+ }
+
+ private List GetTestItems()
+ {
+ return new List
+ {
+ new AccessibleHeaderTestItem { Id = 1, Name = "Alpha" },
+ new AccessibleHeaderTestItem { Id = 2, Name = "Beta" }
+ };
+ }
+
+ public class AccessibleHeaderTestItem
+ {
+ public int Id { get; set; }
+ public string Name { get; set; }
+ }
+}
diff --git a/src/BlazorWebFormsComponents.Test/Image/DescriptionUrl.razor b/src/BlazorWebFormsComponents.Test/Image/DescriptionUrl.razor
new file mode 100644
index 000000000..c2ae720f4
--- /dev/null
+++ b/src/BlazorWebFormsComponents.Test/Image/DescriptionUrl.razor
@@ -0,0 +1,27 @@
+
+@code {
+ [Fact]
+ public void Image_WithDescriptionUrl_RendersLongDescAttribute()
+ {
+ var cut = Render(@ );
+ var img = cut.Find("img");
+ img.HasAttribute("longdesc").ShouldBeTrue();
+ img.GetAttribute("longdesc").ShouldBe("http://example.com/desc");
+ }
+
+ [Fact]
+ public void Image_WithoutDescriptionUrl_NoLongDescAttribute()
+ {
+ var cut = Render(@ );
+ var img = cut.Find("img");
+ img.HasAttribute("longdesc").ShouldBeFalse();
+ }
+
+ [Fact]
+ public void Image_EmptyDescriptionUrl_NoLongDescAttribute()
+ {
+ var cut = Render(@ );
+ var img = cut.Find("img");
+ img.HasAttribute("longdesc").ShouldBeFalse();
+ }
+}
diff --git a/src/BlazorWebFormsComponents.Test/LinkButton/Format.razor b/src/BlazorWebFormsComponents.Test/LinkButton/Format.razor
index 4d471d4fa..7f84dc8f7 100644
--- a/src/BlazorWebFormsComponents.Test/LinkButton/Format.razor
+++ b/src/BlazorWebFormsComponents.Test/LinkButton/Format.razor
@@ -6,4 +6,45 @@ public void LinkButton_RendersAnchor_WithTextAndPostBackUrl()
var cut = Render(@ );
cut.MarkupMatches(@Go to Bing!! );
}
+
+[Fact]
+public void LinkButton_CssClass_RendersClassAttribute()
+{
+ var cut = Render(@ );
+ var a = cut.Find("a");
+ a.GetAttribute("class").ShouldBe("btn btn-primary");
+}
+
+[Fact]
+public void LinkButton_NoCssClass_NoClassAttribute()
+{
+ var cut = Render(@ );
+ var a = cut.Find("a");
+ a.HasAttribute("class").ShouldBeFalse();
+}
+
+[Fact]
+public void LinkButton_Disabled_RendersAspNetDisabledClass()
+{
+ var cut = Render(@ );
+ var a = cut.Find("a");
+ a.GetAttribute("class").ShouldContain("aspNetDisabled");
+}
+
+[Fact]
+public void LinkButton_DisabledWithCssClass_RendersBothClasses()
+{
+ var cut = Render(@ );
+ var a = cut.Find("a");
+ a.GetAttribute("class").ShouldContain("btn");
+ a.GetAttribute("class").ShouldContain("aspNetDisabled");
+}
+
+[Fact]
+public void LinkButton_CssClass_WithPostBackUrl_RendersClassAttribute()
+{
+ var cut = Render(@ );
+ var a = cut.Find("a");
+ a.GetAttribute("class").ShouldBe("my-link");
+}
}
diff --git a/src/BlazorWebFormsComponents/BulletedList.razor b/src/BlazorWebFormsComponents/BulletedList.razor
index 3e5fd63bb..e6aef21a9 100644
--- a/src/BlazorWebFormsComponents/BulletedList.razor
+++ b/src/BlazorWebFormsComponents/BulletedList.razor
@@ -9,7 +9,7 @@
@if (IsOrderedList)
{
-
+
@RenderListItems(allItems)
}
diff --git a/src/BlazorWebFormsComponents/BulletedList.razor.cs b/src/BlazorWebFormsComponents/BulletedList.razor.cs
index 5b6892413..7470f7cc4 100644
--- a/src/BlazorWebFormsComponents/BulletedList.razor.cs
+++ b/src/BlazorWebFormsComponents/BulletedList.razor.cs
@@ -86,17 +86,11 @@ public partial class BulletedList : BaseListControl
};
///
-/// Gets the HTML type attribute value for ordered lists.
+/// Gets the start attribute value for ordered lists, or null when the default (1) is used.
+/// WebForms only renders the start attribute when FirstBulletNumber differs from 1.
///
-protected string OrderedListType => BulletStyle switch
-{
-BulletStyle.Numbered => "1",
-BulletStyle.LowerAlpha => "a",
-BulletStyle.UpperAlpha => "A",
-BulletStyle.LowerRoman => "i",
-BulletStyle.UpperRoman => "I",
-_ => null
-};
+protected int? GetStartAttribute() =>
+ IsOrderedList && FirstBulletNumber != 1 ? FirstBulletNumber : null;
///
/// Gets the combined style string including list-style customization.
diff --git a/src/BlazorWebFormsComponents/GridView.razor.cs b/src/BlazorWebFormsComponents/GridView.razor.cs
index 9d27a610b..bf10aa768 100644
--- a/src/BlazorWebFormsComponents/GridView.razor.cs
+++ b/src/BlazorWebFormsComponents/GridView.razor.cs
@@ -226,8 +226,9 @@ public object SelectedValue
///
/// Gets or sets whether header cells render with th scope="col" for accessibility.
+ /// WebForms defaults this to true.
///
- [Parameter] public bool UseAccessibleHeader { get; set; }
+ [Parameter] public bool UseAccessibleHeader { get; set; } = true;
///
/// Gets or sets the cell padding for the table. -1 means the attribute is not rendered.
diff --git a/src/BlazorWebFormsComponents/LinkButton.razor b/src/BlazorWebFormsComponents/LinkButton.razor
index aa81239dd..965975015 100644
--- a/src/BlazorWebFormsComponents/LinkButton.razor
+++ b/src/BlazorWebFormsComponents/LinkButton.razor
@@ -12,6 +12,8 @@ else
@code {
private string GetCssClassOrNull()
{
+ if (!Enabled)
+ return string.Concat(CssClass, " aspNetDisabled").Trim();
return !string.IsNullOrEmpty(CssClass) ? CssClass : null;
}
}