a2e2f4b463
The trailing disabled="@Busy" overrode the splatted attribute, so a caller's disabled was dropped whenever Busy was false and the button stayed clickable. The effective state is now Busy || caller disabled (Blazor splat semantics). - tests: caller disabled (true/"disabled"/""), false, and re-render toggle - Directory.Build.props 0.5.0 -> 0.5.1 (NOT packed or published) - code-reviews: Theme-007 Resolved; README regenerated Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
103 lines
3.8 KiB
C#
103 lines
3.8 KiB
C#
namespace ZB.MOM.WW.Theme.Tests;
|
|
|
|
public sealed class TechButtonExtensionsTests : BunitContext
|
|
{
|
|
[Theory]
|
|
[InlineData(ButtonSize.Default, null)]
|
|
[InlineData(ButtonSize.Small, "btn-sm")]
|
|
[InlineData(ButtonSize.Large, "btn-lg")]
|
|
public void Size_preserves_variant_and_composes_both_class_inputs(ButtonSize size, string? expected)
|
|
{
|
|
var cut = Render<TechButton>(p => p
|
|
.Add(x => x.Variant, ButtonVariant.Secondary)
|
|
.Add(x => x.Size, size)
|
|
.Add(x => x.Class, "explicit-action")
|
|
.Add(x => x.AdditionalAttributes, new Dictionary<string, object>
|
|
{
|
|
["class"] = "caller-action", ["aria-label"] = "Save changes"
|
|
})
|
|
.AddChildContent("Save"));
|
|
|
|
var button = cut.Find("button");
|
|
foreach (var cls in new[] { "btn", "btn-outline-secondary", "explicit-action", "caller-action" })
|
|
Assert.Contains(cls, button.ClassList);
|
|
Assert.Equal("Save changes", button.GetAttribute("aria-label"));
|
|
if (expected is not null) Assert.Contains(expected, button.ClassList);
|
|
else
|
|
{
|
|
Assert.DoesNotContain("btn-sm", button.ClassList);
|
|
Assert.DoesNotContain("btn-lg", button.ClassList);
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public void Class_composition_keeps_busy_type_and_click_behavior()
|
|
{
|
|
var clicks = 0;
|
|
var attributes = new Dictionary<string, object>
|
|
{
|
|
["class"] = "caller", ["disabled"] = false,
|
|
["onclick"] = EventCallback.Factory.Create(this, () => clicks++)
|
|
};
|
|
var cut = Render<TechButton>(p => p
|
|
.Add(x => x.Class, "extra")
|
|
.Add(x => x.Type, "submit")
|
|
.Add(x => x.Busy, true)
|
|
.Add(x => x.AdditionalAttributes, attributes)
|
|
.AddChildContent("Save"));
|
|
|
|
Assert.True(cut.Find("button").HasAttribute("disabled"));
|
|
Assert.Equal("submit", cut.Find("button").GetAttribute("type"));
|
|
Assert.NotNull(cut.Find(".spinner-border"));
|
|
Assert.Equal("caller", attributes["class"]);
|
|
cut.Render(p => p.Add(x => x.Busy, false));
|
|
cut.Find("button").Click();
|
|
Assert.Equal(1, clicks);
|
|
Assert.DoesNotContain(cut.FindAll("span"), x => x.ClassList.Contains("spinner-border"));
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData(true)]
|
|
[InlineData("disabled")]
|
|
[InlineData("")]
|
|
public void Caller_disabled_is_honoured_when_not_busy(object disabled)
|
|
{
|
|
// Theme-007 (scadaproj#44): the trailing disabled="@Busy" overwrote the splatted value, so
|
|
// a caller's disabled was silently dropped whenever Busy was false.
|
|
var cut = Render<TechButton>(p => p
|
|
.AddUnmatched("disabled", disabled)
|
|
.AddChildContent("Delete"));
|
|
|
|
var button = cut.Find("button");
|
|
Assert.True(button.HasAttribute("disabled"));
|
|
Assert.Empty(cut.FindAll(".spinner-border"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Caller_disabled_false_leaves_the_button_enabled()
|
|
{
|
|
var cut = Render<TechButton>(p => p.AddUnmatched("disabled", false).AddChildContent("Go"));
|
|
|
|
Assert.False(cut.Find("button").HasAttribute("disabled"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Caller_disabled_toggles_off_on_rerender()
|
|
{
|
|
var cut = Render<TechButton>(p => p.AddUnmatched("disabled", true).AddChildContent("Go"));
|
|
Assert.True(cut.Find("button").HasAttribute("disabled"));
|
|
|
|
cut.Render(p => p.AddUnmatched("disabled", false));
|
|
|
|
Assert.False(cut.Find("button").HasAttribute("disabled"));
|
|
}
|
|
|
|
[Fact]
|
|
public void Lowercase_class_attribute_preserves_required_button_classes()
|
|
{
|
|
var cut = Render<TechButton>(p => p.AddUnmatched("class", "caller").AddChildContent("Go"));
|
|
foreach (var cls in new[] { "btn", "btn-primary", "caller" })
|
|
Assert.Contains(cls, cut.Find("button").ClassList);
|
|
}
|
|
}
|