Skip to content

[release/8.0] [Blazor] Blazor InputRadio is broken by IHandleEvent #53270

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions src/Components/Web/src/Forms/IInputRadioValueProvider.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.AspNetCore.Components;

internal interface IInputRadioValueProvider
{
public object? CurrentValue { get; }
}
12 changes: 5 additions & 7 deletions src/Components/Web/src/Forms/InputRadioContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,19 @@ namespace Microsoft.AspNetCore.Components.Forms;
/// </summary>
internal sealed class InputRadioContext
{
private readonly IInputRadioValueProvider _valueProvider;

public InputRadioContext? ParentContext { get; }
public EventCallback<ChangeEventArgs> ChangeEventCallback { get; }
public object? CurrentValue => _valueProvider.CurrentValue;

// Mutable properties that may change any time an InputRadioGroup is rendered
public string? GroupName { get; set; }
public object? CurrentValue { get; set; }
public string? FieldClass { get; set; }

/// <summary>
/// Instantiates a new <see cref="InputRadioContext" />.
/// </summary>
/// <param name="parentContext">The parent context, if any.</param>
/// <param name="changeEventCallback">The event callback to be invoked when the selected value is changed.</param>
public InputRadioContext(InputRadioContext? parentContext, EventCallback<ChangeEventArgs> changeEventCallback)
public InputRadioContext(IInputRadioValueProvider valueProvider, InputRadioContext? parentContext, EventCallback<ChangeEventArgs> changeEventCallback)
{
_valueProvider = valueProvider;
ParentContext = parentContext;
ChangeEventCallback = changeEventCallback;
}
Expand Down
8 changes: 5 additions & 3 deletions src/Components/Web/src/Forms/InputRadioGroup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Components.Forms;
/// <summary>
/// Groups child <see cref="InputRadio{TValue}"/> components.
/// </summary>
public class InputRadioGroup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TValue> : InputBase<TValue>
public class InputRadioGroup<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] TValue> : InputBase<TValue>, IInputRadioValueProvider
{
private readonly string _defaultGroupName = Guid.NewGuid().ToString("N");
private InputRadioContext? _context;
Expand All @@ -27,14 +27,16 @@ public class InputRadioGroup<[DynamicallyAccessedMembers(DynamicallyAccessedMemb

[CascadingParameter] private InputRadioContext? CascadedContext { get; set; }

object? IInputRadioValueProvider.CurrentValue => CurrentValue;

/// <inheritdoc />
protected override void OnParametersSet()
{
// On the first render, we can instantiate the InputRadioContext
if (_context is null)
{
var changeEventCallback = EventCallback.Factory.CreateBinder<string?>(this, __value => CurrentValueAsString = __value, CurrentValueAsString);
_context = new InputRadioContext(CascadedContext, changeEventCallback);
_context = new InputRadioContext(this, CascadedContext, changeEventCallback);
}
else if (_context.ParentContext != CascadedContext)
{
Expand All @@ -59,7 +61,7 @@ protected override void OnParametersSet()
// Otherwise, just use a GUID to disambiguate this group's radio inputs from any others on the page.
_context.GroupName = _defaultGroupName;
}
_context.CurrentValue = CurrentValue;

_context.FieldClass = EditContext?.FieldCssClass(FieldIdentifier);
}

Expand Down
24 changes: 24 additions & 0 deletions src/Components/test/E2ETest/Tests/FormsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,30 @@ public void InputRadioGroupWorksWithMutatingSetter()
Browser.Equal("False", () => tuesday.GetDomProperty("checked"));
}

[Theory]
[InlineData(0)]
[InlineData(2)]
public void InputRadioGroupWorksWithParentImplementingIHandleEvent(int n)
{
Browser.Url = new UriBuilder(Browser.Url) { Query = ($"?n={n}") }.ToString();
var appElement = Browser.MountTestComponent<InputRadioParentImplementsIHandleEvent>();
var zero = appElement.FindElement(By.Id("inputradiogroup-parent-ihandle-event-0"));
var one = appElement.FindElement(By.Id("inputradiogroup-parent-ihandle-event-1"));

Browser.Equal(n == 0 ? "True" : "False", () => zero.GetDomProperty("checked"));
Browser.Equal("False", () => one.GetDomProperty("checked"));

// Observe the changes after a click
one.Click();
Browser.Equal("False", () => zero.GetDomProperty("checked"));
Browser.Equal("True", () => one.GetDomProperty("checked"));

// Ensure other options can be selected
zero.Click();
Browser.Equal("False", () => one.GetDomProperty("checked"));
Browser.Equal("True", () => zero.GetDomProperty("checked"));
}

[Fact]
public void InputSelectWorksWithMutatingSetter()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@using Microsoft.AspNetCore.Components.Forms
@implements IHandleEvent

<InputRadioGroup @bind-Value="N">
<InputRadio id="inputradiogroup-parent-ihandle-event-0" Value="0" />
<InputRadio id="inputradiogroup-parent-ihandle-event-1" Value="1" />
</InputRadioGroup>

@code {

[SupplyParameterFromQuery(Name = "n")] int? N { get; set; }

Task IHandleEvent.HandleEventAsync(EventCallbackWorkItem callback, object arg) => callback.InvokeAsync(arg);
}
1 change: 1 addition & 0 deletions src/Components/test/testassets/BasicTestApp/Index.razor
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
<option value="BasicTestApp.FormsTest.InputRangeComponent">Input range</option>
<option value="BasicTestApp.FormsTest.InputsWithoutEditForm">Inputs without EditForm</option>
<option value="BasicTestApp.FormsTest.InputsWithMutatingSetters">Inputs with mutating setters</option>
<option value="BasicTestApp.FormsTest.InputRadioParentImplementsIHandleEvent">Input Radio Parent Implements IHandleEvent</option>
<option value="BasicTestApp.NavigateOnSubmit">Navigate to submit</option>
<option value="BasicTestApp.GlobalizationBindCases">Globalization Bind Cases</option>
<option value="BasicTestApp.GracefulTermination">Graceful Termination</option>
Expand Down