From eb8d708a8eda9e0772f9c3208c99926b12d7cb45 Mon Sep 17 00:00:00 2001 From: m-akinc <7282195+m-akinc@users.noreply.github.com> Date: Thu, 29 Sep 2022 14:26:22 -0500 Subject: [PATCH] Blazor integration for radio button and group (#754) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 🤨 Rationale Blazor integration for radio button and radio group. ## 👩‍💻 Implementation - Copied and modified the Blazor wrapper for the FluentRadio and FluentRadioGroup - Added Orientation type for radio group's orientation property - Created a few tests, following the example of other Blazor wrappers - Added a radio group to the Blazor example app - Updated README table from spreadsheet ## 🧪 Testing Unit tests and example app ## ✅ Checklist - [x] I have updated the project documentation to reflect my changes or determined no changes are needed. --- ...-edc0cd68-5b88-4b6b-87e0-710ca16d49b0.json | 7 ++ .../Demo.Shared/Pages/ComponentsDemo.razor | 9 +++ .../Demo.Shared/Pages/ComponentsDemo.razor.cs | 1 + .../Components/NimbleRadioButton.razor | 8 ++ .../Components/NimbleRadioButton.razor.cs | 37 +++++++++ .../Components/NimbleRadioGroup.razor | 10 +++ .../Components/NimbleRadioGroup.razor.cs | 38 +++++++++ .../nimble-blazor/NimbleBlazor/Orientation.cs | 14 ++++ .../Unit/Components/NimbleRadioButtonTests.cs | 55 +++++++++++++ .../Unit/Components/NimbleRadioGroupTests.cs | 80 +++++++++++++++++++ 10 files changed, 259 insertions(+) create mode 100644 change/@ni-nimble-blazor-edc0cd68-5b88-4b6b-87e0-710ca16d49b0.json create mode 100644 packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioButton.razor create mode 100644 packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioButton.razor.cs create mode 100644 packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioGroup.razor create mode 100644 packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioGroup.razor.cs create mode 100644 packages/nimble-blazor/NimbleBlazor/Orientation.cs create mode 100644 packages/nimble-blazor/Tests/NimbleBlazor.Tests/Unit/Components/NimbleRadioButtonTests.cs create mode 100644 packages/nimble-blazor/Tests/NimbleBlazor.Tests/Unit/Components/NimbleRadioGroupTests.cs diff --git a/change/@ni-nimble-blazor-edc0cd68-5b88-4b6b-87e0-710ca16d49b0.json b/change/@ni-nimble-blazor-edc0cd68-5b88-4b6b-87e0-710ca16d49b0.json new file mode 100644 index 0000000000..3f48662175 --- /dev/null +++ b/change/@ni-nimble-blazor-edc0cd68-5b88-4b6b-87e0-710ca16d49b0.json @@ -0,0 +1,7 @@ +{ + "type": "minor", + "comment": "Blazor wrapper for radio button and group", + "packageName": "@ni/nimble-blazor", + "email": "7282195+m-akinc@users.noreply.github.com", + "dependentChangeType": "patch" +} diff --git a/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor b/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor index f800a3c4e2..574d28ed05 100644 --- a/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor +++ b/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor @@ -49,6 +49,15 @@ Checkbox label Checkbox label +
+
Radio Buttons
+ + Option 1 + Option 2 + Option 3 + + Value +
Dialog:
Open Dialog diff --git a/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor.cs b/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor.cs index 995b8a7013..07f3b0a779 100644 --- a/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor.cs +++ b/packages/nimble-blazor/Examples/Demo.Shared/Pages/ComponentsDemo.razor.cs @@ -12,6 +12,7 @@ public partial class ComponentsDemo private string? ActiveTabId { get; set; } private NimbleDialog? _dialog; private string? ClosedReason { get; set; } + private string? SelectedRadio { get; set; } = "2"; private string DrawerLocationAsString { diff --git a/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioButton.razor b/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioButton.razor new file mode 100644 index 0000000000..8948513863 --- /dev/null +++ b/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioButton.razor @@ -0,0 +1,8 @@ +@namespace NimbleBlazor +@inherits ComponentBase + + @ChildContent + \ No newline at end of file diff --git a/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioButton.razor.cs b/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioButton.razor.cs new file mode 100644 index 0000000000..fc3e1837dd --- /dev/null +++ b/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioButton.razor.cs @@ -0,0 +1,37 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Components; + +namespace NimbleBlazor; + +public partial class NimbleRadioButton : ComponentBase +{ + /// + /// Gets or sets the value. + /// + [Parameter] + public string? CurrentValue { get; set; } + + /// + /// Gets or sets the disabled state. + /// + [Parameter] + public bool Disabled { get; set; } + + /// + /// Gets or sets the name. + /// + [Parameter] + public string? Name { get; set; } + + /// + /// Gets or sets the child content to be rendered inside the . + /// + [Parameter] + public RenderFragment? ChildContent { get; set; } + + /// + /// Gets or sets any additional attributes to set on the element. + /// + [Parameter(CaptureUnmatchedValues = true)] + public IDictionary? AdditionalAttributes { get; set; } +} diff --git a/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioGroup.razor b/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioGroup.razor new file mode 100644 index 0000000000..7126ca671b --- /dev/null +++ b/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioGroup.razor @@ -0,0 +1,10 @@ +@namespace NimbleBlazor +@inherits NimbleInputBase + + @ChildContent + \ No newline at end of file diff --git a/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioGroup.razor.cs b/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioGroup.razor.cs new file mode 100644 index 0000000000..2409272603 --- /dev/null +++ b/packages/nimble-blazor/NimbleBlazor/Components/NimbleRadioGroup.razor.cs @@ -0,0 +1,38 @@ +using System.Diagnostics.CodeAnalysis; +using Microsoft.AspNetCore.Components; + +namespace NimbleBlazor; + +public partial class NimbleRadioGroup : NimbleInputBase +{ + /// + /// Gets or sets the disabled state. + /// + [Parameter] + public bool? Disabled { get; set; } + + /// + /// Gets or sets the name. + /// + [Parameter] + public string? Name { get; set; } + + /// + /// Gets or sets the Orientation of the group. + /// + [Parameter] + public Orientation? Orientation { get; set; } + + /// + /// Gets or sets the child content to be rendered inside the . + /// + [Parameter] + public RenderFragment? ChildContent { get; set; } + + protected override bool TryParseValueFromString(string? value, [MaybeNullWhen(false)] out string result, [NotNullWhen(false)] out string? validationErrorMessage) + { + result = value ?? string.Empty; + validationErrorMessage = null; + return true; + } +} diff --git a/packages/nimble-blazor/NimbleBlazor/Orientation.cs b/packages/nimble-blazor/NimbleBlazor/Orientation.cs new file mode 100644 index 0000000000..fa251401bb --- /dev/null +++ b/packages/nimble-blazor/NimbleBlazor/Orientation.cs @@ -0,0 +1,14 @@ +namespace NimbleBlazor; + +public enum Orientation +{ + Horizontal, + Vertical +} + +internal static class OrientationExtensions +{ + private static readonly Dictionary _orientationValues = AttributeHelpers.GetEnumNamesAsKebabCaseValues(); + + public static string? ToAttributeValue(this Orientation? value) => value == null ? null : _orientationValues[value.Value]; +} \ No newline at end of file diff --git a/packages/nimble-blazor/Tests/NimbleBlazor.Tests/Unit/Components/NimbleRadioButtonTests.cs b/packages/nimble-blazor/Tests/NimbleBlazor.Tests/Unit/Components/NimbleRadioButtonTests.cs new file mode 100644 index 0000000000..10998f4552 --- /dev/null +++ b/packages/nimble-blazor/Tests/NimbleBlazor.Tests/Unit/Components/NimbleRadioButtonTests.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq.Expressions; +using Bunit; +using Xunit; + +namespace NimbleBlazor.Tests.Unit.Components; + +/// +/// Tests for +/// +public class NimbleRadioButtonTests +{ + [Fact] + public void NimbleRadioButton_Rendered_HasRadioButtonMarkup() + { + var context = new TestContext(); + context.JSInterop.Mode = JSRuntimeMode.Loose; + var expectedMarkup = "nimble-radio-button"; + + var radioButton = context.RenderComponent(); + + Assert.Contains(expectedMarkup, radioButton.Markup); + } + + [Fact] + public void NimbleRadioButtonCurrentValue_AttributeIsSet() + { + var radioButton = RenderNimbleRadioButtonWithPropertySet(x => x.CurrentValue, "foo"); + + Assert.Contains("current-value", radioButton.Markup); + } + + [Fact] + public void NimbleRadioButtonDisabled_AttributeIsSet() + { + var radioButton = RenderNimbleRadioButtonWithPropertySet(x => x.Disabled, true); + + Assert.Contains("disabled", radioButton.Markup); + } + + [Fact] + public void NimbleRadioButtonName_AttributeIsSet() + { + var radioButton = RenderNimbleRadioButtonWithPropertySet(x => x.Name, "buttons"); + + Assert.Contains("name", radioButton.Markup); + } + + private IRenderedComponent RenderNimbleRadioButtonWithPropertySet(Expression> propertyGetter, TProperty propertyValue) + { + var context = new TestContext(); + context.JSInterop.Mode = JSRuntimeMode.Loose; + return context.RenderComponent(p => p.Add(propertyGetter, propertyValue)); + } +} diff --git a/packages/nimble-blazor/Tests/NimbleBlazor.Tests/Unit/Components/NimbleRadioGroupTests.cs b/packages/nimble-blazor/Tests/NimbleBlazor.Tests/Unit/Components/NimbleRadioGroupTests.cs new file mode 100644 index 0000000000..c3382741da --- /dev/null +++ b/packages/nimble-blazor/Tests/NimbleBlazor.Tests/Unit/Components/NimbleRadioGroupTests.cs @@ -0,0 +1,80 @@ +using System; +using System.Linq.Expressions; +using Bunit; +using Xunit; + +namespace NimbleBlazor.Tests.Unit.Components; + +/// +/// Tests for +/// +public class NimbleRadioGroupTests +{ + [Fact] + public void NimbleRadioGroup_Rendered_HasRadioGroupMarkup() + { + var context = new TestContext(); + context.JSInterop.Mode = JSRuntimeMode.Loose; + var expectedMarkup = "nimble-radio-group"; + + var radioGroup = context.RenderComponent(); + + Assert.Contains(expectedMarkup, radioGroup.Markup); + } + + [Theory] + [InlineData(Orientation.Horizontal, "horizontal")] + [InlineData(Orientation.Vertical, "vertical")] + public void RadioGroupOrientation_AttributeIsSet(Orientation value, string expectedAttribute) + { + var radioGroup = RenderNimbleRadioGroup(value); + + Assert.Contains(expectedAttribute, radioGroup.Markup); + } + + [Fact] + public void RadioGroupWithButton_HasRadioButtonMarkup() + { + var expectedMarkup = "nimble-radio-button"; + var select = RenderNimbleRadioGroupWithButton(); + + Assert.Contains(expectedMarkup, select.Markup); + } + + [Fact] + public void NimbleRadioGroupDisabled_AttributeIsSet() + { + var radioGroup = RenderNimbleRadioGroupWithPropertySet(x => x.Disabled, true); + + Assert.Contains("disabled", radioGroup.Markup); + } + + [Fact] + public void NimbleRadioGroupName_AttributeIsSet() + { + var radioGroup = RenderNimbleRadioGroupWithPropertySet(x => x.Name, "foo"); + + Assert.Contains("name", radioGroup.Markup); + } + + private IRenderedComponent RenderNimbleRadioGroupWithPropertySet(Expression> propertyGetter, TProperty propertyValue) + { + var context = new TestContext(); + context.JSInterop.Mode = JSRuntimeMode.Loose; + return context.RenderComponent(p => p.Add(propertyGetter, propertyValue)); + } + + private IRenderedComponent RenderNimbleRadioGroupWithButton() + { + var context = new TestContext(); + context.JSInterop.Mode = JSRuntimeMode.Loose; + return context.RenderComponent(p => p.AddChildContent()); + } + + private IRenderedComponent RenderNimbleRadioGroup(Orientation orientation) + { + var context = new TestContext(); + context.JSInterop.Mode = JSRuntimeMode.Loose; + return context.RenderComponent(p => p.Add(x => x.Orientation, orientation)); + } +}