diff --git a/src/Components/Web/src/Forms/InputSelect.cs b/src/Components/Web/src/Forms/InputSelect.cs
index b8cdbeab1f..e1a3ab0eae 100644
--- a/src/Components/Web/src/Forms/InputSelect.cs
+++ b/src/Components/Web/src/Forms/InputSelect.cs
@@ -17,6 +17,8 @@ namespace Microsoft.AspNetCore.Components.Forms
///
[Parameter] public RenderFragment ChildContent { get; set; }
+ private readonly Type _nullableUnderlyingType = Nullable.GetUnderlyingType(typeof(TValue));
+
///
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
@@ -38,7 +40,7 @@ namespace Microsoft.AspNetCore.Components.Forms
validationErrorMessage = null;
return true;
}
- else if (typeof(TValue).IsEnum)
+ else if (typeof(TValue).IsEnum || (_nullableUnderlyingType != null && _nullableUnderlyingType.IsEnum))
{
var success = BindConverter.TryConvertTo(value, CultureInfo.CurrentCulture, out var parsedValue);
if (success)
diff --git a/src/Components/Web/test/Forms/InputSelectTest.cs b/src/Components/Web/test/Forms/InputSelectTest.cs
new file mode 100644
index 0000000000..7c49fe03be
--- /dev/null
+++ b/src/Components/Web/test/Forms/InputSelectTest.cs
@@ -0,0 +1,153 @@
+// Copyright (c) .NET Foundation. All rights reserved.
+// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+
+using System;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Components.Rendering;
+using Microsoft.AspNetCore.Components.RenderTree;
+using Microsoft.AspNetCore.Components.Test.Helpers;
+using Xunit;
+
+namespace Microsoft.AspNetCore.Components.Forms
+{
+ public class InputSelectTest
+ {
+ [Fact]
+ public async Task ParsesCurrentValueWhenUsingNotNullableEnumWithNotEmptyValue()
+ {
+ // Arrange
+ var model = new TestModel();
+ var rootComponent = new TestInputSelectHostComponent
+ {
+ EditContext = new EditContext(model),
+ ValueExpression = () => model.NotNullableEnum
+ };
+ var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
+
+ // Act
+ inputSelectComponent.CurrentValueAsString = "Two";
+
+ // Assert
+ Assert.Equal(TestEnum.Two, inputSelectComponent.CurrentValue);
+ }
+
+ [Fact]
+ public async Task ParsesCurrentValueWhenUsingNotNullableEnumWithEmptyValue()
+ {
+ // Arrange
+ var model = new TestModel();
+ var rootComponent = new TestInputSelectHostComponent
+ {
+ EditContext = new EditContext(model),
+ ValueExpression = () => model.NotNullableEnum
+ };
+ var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
+
+ // Act
+ inputSelectComponent.CurrentValueAsString = "";
+
+ // Assert
+ Assert.Equal(default, inputSelectComponent.CurrentValue);
+ }
+
+ [Fact]
+ public async Task ParsesCurrentValueWhenUsingNullableEnumWithNotEmptyValue()
+ {
+ // Arrange
+ var model = new TestModel();
+ var rootComponent = new TestInputSelectHostComponent
+ {
+ EditContext = new EditContext(model),
+ ValueExpression = () => model.NullableEnum
+ };
+ var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
+
+ // Act
+ inputSelectComponent.CurrentValueAsString = "Two";
+
+ // Assert
+ Assert.Equal(TestEnum.Two, inputSelectComponent.Value);
+ }
+
+ [Fact]
+ public async Task ParsesCurrentValueWhenUsingNullableEnumWithEmptyValue()
+ {
+ // Arrange
+ var model = new TestModel();
+ var rootComponent = new TestInputSelectHostComponent
+ {
+ EditContext = new EditContext(model),
+ ValueExpression = () => model.NullableEnum
+ };
+ var inputSelectComponent = await RenderAndGetTestInputComponentAsync(rootComponent);
+
+ // Act
+ inputSelectComponent.CurrentValueAsString = "";
+
+ // Assert
+ Assert.Null(inputSelectComponent.CurrentValue);
+ }
+
+ private static TestInputSelect FindInputSelectComponent(CapturedBatch batch)
+ => batch.ReferenceFrames
+ .Where(f => f.FrameType == RenderTreeFrameType.Component)
+ .Select(f => f.Component)
+ .OfType>()
+ .Single();
+
+ private static async Task> RenderAndGetTestInputComponentAsync(TestInputSelectHostComponent hostComponent)
+ {
+ var testRenderer = new TestRenderer();
+ var componentId = testRenderer.AssignRootComponentId(hostComponent);
+ await testRenderer.RenderRootComponentAsync(componentId);
+ return FindInputSelectComponent(testRenderer.Batches.Single());
+ }
+
+ enum TestEnum
+ {
+ One,
+ Two,
+ Tree
+ }
+
+ class TestModel
+ {
+ public TestEnum NotNullableEnum { get; set; }
+
+ public TestEnum? NullableEnum { get; set; }
+ }
+
+ class TestInputSelect : InputSelect
+ {
+ public new TValue CurrentValue => base.CurrentValue;
+
+ public new string CurrentValueAsString
+ {
+ get => base.CurrentValueAsString;
+ set => base.CurrentValueAsString = value;
+ }
+ }
+
+ class TestInputSelectHostComponent : AutoRenderComponent
+ {
+ public EditContext EditContext { get; set; }
+
+ public Expression> ValueExpression { get; set; }
+
+ protected override void BuildRenderTree(RenderTreeBuilder builder)
+ {
+ builder.OpenComponent>(0);
+ builder.AddAttribute(1, "Value", EditContext);
+ builder.AddAttribute(2, "ChildContent", new RenderFragment(childBuilder =>
+ {
+ childBuilder.OpenComponent>(0);
+ childBuilder.AddAttribute(0, "ValueExpression", ValueExpression);
+ childBuilder.CloseComponent();
+ }));
+ builder.CloseComponent();
+ }
+ }
+ }
+}