feat(theme): TechButton/TechCard/TechField
This commit is contained in:
@@ -0,0 +1,3 @@
|
|||||||
|
namespace ZB.MOM.WW.Theme;
|
||||||
|
|
||||||
|
public enum ButtonVariant { Primary, Secondary, Danger, Ghost }
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
@namespace ZB.MOM.WW.Theme
|
||||||
|
@* Components/TechButton.razor *@
|
||||||
|
<button type="@Type" class="btn @VariantClass" disabled="@Busy" @attributes="Extra">
|
||||||
|
@if (Busy) { <span class="spinner-border spinner-border-sm me-1" aria-hidden="true"></span> }
|
||||||
|
@ChildContent
|
||||||
|
</button>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public ButtonVariant Variant { get; set; } = ButtonVariant.Primary;
|
||||||
|
[Parameter] public string Type { get; set; } = "button";
|
||||||
|
[Parameter] public bool Busy { get; set; }
|
||||||
|
[Parameter] public RenderFragment? ChildContent { get; set; }
|
||||||
|
[Parameter(CaptureUnmatchedValues = true)] public IDictionary<string, object>? Extra { get; set; }
|
||||||
|
|
||||||
|
private string VariantClass => Variant switch
|
||||||
|
{
|
||||||
|
ButtonVariant.Secondary => "btn-outline-secondary",
|
||||||
|
ButtonVariant.Danger => "btn-danger",
|
||||||
|
ButtonVariant.Ghost => "btn-link",
|
||||||
|
_ => "btn-primary",
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
@namespace ZB.MOM.WW.Theme
|
||||||
|
@* Components/TechCard.razor *@
|
||||||
|
<section class="panel @Class">
|
||||||
|
@if (Header is not null) { <div class="panel-head">@Header</div> }
|
||||||
|
else if (!string.IsNullOrEmpty(Title)) { <div class="panel-head">@Title</div> }
|
||||||
|
<div class="panel-body">@ChildContent</div>
|
||||||
|
@if (Footer is not null) { <div class="panel-foot">@Footer</div> }
|
||||||
|
</section>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter] public string? Title { get; set; }
|
||||||
|
[Parameter] public RenderFragment? Header { get; set; }
|
||||||
|
[Parameter] public RenderFragment? ChildContent { get; set; }
|
||||||
|
[Parameter] public RenderFragment? Footer { get; set; }
|
||||||
|
[Parameter] public string? Class { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
@namespace ZB.MOM.WW.Theme
|
||||||
|
@* Components/TechField.razor *@
|
||||||
|
<div class="tech-field mb-3">
|
||||||
|
<label class="form-label">@Label</label>
|
||||||
|
@ChildContent
|
||||||
|
@if (!string.IsNullOrEmpty(Hint)) { <div class="form-text">@Hint</div> }
|
||||||
|
@if (!string.IsNullOrEmpty(Error)) { <div class="field-error s-bad">@Error</div> }
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter, EditorRequired] public string Label { get; set; } = string.Empty;
|
||||||
|
[Parameter] public string? Hint { get; set; }
|
||||||
|
[Parameter] public string? Error { get; set; }
|
||||||
|
[Parameter] public RenderFragment? ChildContent { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
namespace ZB.MOM.WW.Theme.Tests;
|
||||||
|
|
||||||
|
public class CommonControlsTests : TestContext
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(ButtonVariant.Primary, "btn-primary")]
|
||||||
|
[InlineData(ButtonVariant.Secondary, "btn-outline-secondary")]
|
||||||
|
[InlineData(ButtonVariant.Danger, "btn-danger")]
|
||||||
|
[InlineData(ButtonVariant.Ghost, "btn-link")]
|
||||||
|
public void TechButton_maps_variant(ButtonVariant v, string cls)
|
||||||
|
{
|
||||||
|
var cut = RenderComponent<TechButton>(p => p.Add(x => x.Variant, v).AddChildContent("Go"));
|
||||||
|
var btn = cut.Find("button");
|
||||||
|
Assert.Contains("btn", btn.ClassList);
|
||||||
|
Assert.Contains(cls, btn.ClassList);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TechButton_busy_disables_and_passes_through_attributes()
|
||||||
|
{
|
||||||
|
var cut = RenderComponent<TechButton>(p => p
|
||||||
|
.Add(x => x.Busy, true)
|
||||||
|
.AddUnmatched("id", "save")
|
||||||
|
.AddChildContent("Save"));
|
||||||
|
var btn = cut.Find("button");
|
||||||
|
Assert.True(btn.HasAttribute("disabled"));
|
||||||
|
Assert.Equal("save", btn.GetAttribute("id"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TechCard_renders_title_and_body()
|
||||||
|
{
|
||||||
|
var cut = RenderComponent<TechCard>(p => p
|
||||||
|
.Add(x => x.Title, "Drivers")
|
||||||
|
.AddChildContent("<div class='b'>x</div>"));
|
||||||
|
Assert.Contains("Drivers", cut.Find(".panel-head").TextContent);
|
||||||
|
Assert.NotNull(cut.Find(".panel-body .b"));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void TechField_renders_label_hint_error()
|
||||||
|
{
|
||||||
|
var cut = RenderComponent<TechField>(p => p
|
||||||
|
.Add(x => x.Label, "Name")
|
||||||
|
.Add(x => x.Hint, "required")
|
||||||
|
.Add(x => x.Error, "missing")
|
||||||
|
.AddChildContent("<input/>"));
|
||||||
|
Assert.Contains("Name", cut.Find("label").TextContent);
|
||||||
|
Assert.Contains("required", cut.Find(".form-text").TextContent);
|
||||||
|
Assert.Contains("missing", cut.Find(".field-error").TextContent);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user