ElementReference FocusAsync API (#23316)
* Added working focus extension method. * Added return value documentation for FocusAsync(). * Removed IJSRuntime argument from FocusAsync(). * Removed ElementReference.JSRuntime in favor of IServiceProvider. * Updated Web.JS release binaries. * Implemented ElementReferenceContext. * Made ElementReferenceContext a non-abstract property in Renderer. * Made ElementReference.Context explicitly nullable. * Removed useless IServiceProvider dependency in RemoteJSRuntime. * Updated Microsoft.AspNetCore.Components reference assemblies. * Improved documentation and limited public API.
This commit is contained in:
parent
7c0f02a04d
commit
36c6c2c2a6
|
|
@ -125,8 +125,14 @@ namespace Microsoft.AspNetCore.Components
|
|||
private readonly object _dummy;
|
||||
private readonly int _dummyPrimitive;
|
||||
public ElementReference(string id) { throw null; }
|
||||
public ElementReference(string id, Microsoft.AspNetCore.Components.ElementReferenceContext? context) { throw null; }
|
||||
public Microsoft.AspNetCore.Components.ElementReferenceContext? Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
public string Id { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
}
|
||||
public abstract partial class ElementReferenceContext
|
||||
{
|
||||
protected ElementReferenceContext() { }
|
||||
}
|
||||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||
public readonly partial struct EventCallback
|
||||
{
|
||||
|
|
@ -468,6 +474,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
|||
{
|
||||
public Renderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
|
||||
public abstract Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get; }
|
||||
protected internal Microsoft.AspNetCore.Components.ElementReferenceContext? ElementReferenceContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] protected set { } }
|
||||
public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } }
|
||||
protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; }
|
||||
public virtual System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.RenderTree.EventFieldInfo fieldInfo, System.EventArgs eventArgs) { throw null; }
|
||||
|
|
|
|||
|
|
@ -125,8 +125,14 @@ namespace Microsoft.AspNetCore.Components
|
|||
private readonly object _dummy;
|
||||
private readonly int _dummyPrimitive;
|
||||
public ElementReference(string id) { throw null; }
|
||||
public ElementReference(string id, Microsoft.AspNetCore.Components.ElementReferenceContext? context) { throw null; }
|
||||
public Microsoft.AspNetCore.Components.ElementReferenceContext? Context { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
public string Id { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
}
|
||||
public abstract partial class ElementReferenceContext
|
||||
{
|
||||
protected ElementReferenceContext() { }
|
||||
}
|
||||
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||
public readonly partial struct EventCallback
|
||||
{
|
||||
|
|
@ -467,6 +473,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
|||
{
|
||||
public Renderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
|
||||
public abstract Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get; }
|
||||
protected internal Microsoft.AspNetCore.Components.ElementReferenceContext? ElementReferenceContext { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } [System.Runtime.CompilerServices.CompilerGeneratedAttribute] protected set { } }
|
||||
public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } }
|
||||
protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; }
|
||||
public virtual System.Threading.Tasks.Task DispatchEventAsync(ulong eventHandlerId, Microsoft.AspNetCore.Components.RenderTree.EventFieldInfo fieldInfo, System.EventArgs eventArgs) { throw null; }
|
||||
|
|
|
|||
|
|
@ -23,13 +23,32 @@ namespace Microsoft.AspNetCore.Components
|
|||
/// </remarks>
|
||||
public string Id { get; }
|
||||
|
||||
public ElementReference(string id)
|
||||
/// <summary>
|
||||
/// Gets the <see cref="ElementReferenceContext"/> instance.
|
||||
/// </summary>
|
||||
public ElementReferenceContext? Context { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="ElementReference" />.
|
||||
/// </summary>
|
||||
/// <param name="id">A unique identifier for this <see cref="ElementReference"/>.</param>
|
||||
/// <param name="context">The nullable <see cref="ElementReferenceContext"/> instance.</param>
|
||||
public ElementReference(string id, ElementReferenceContext? context)
|
||||
{
|
||||
Id = id;
|
||||
Context = context;
|
||||
}
|
||||
|
||||
internal static ElementReference CreateWithUniqueId()
|
||||
=> new ElementReference(CreateUniqueId());
|
||||
/// <summary>
|
||||
/// Instantiates a new <see cref="ElementReference"/>.
|
||||
/// </summary>
|
||||
/// <param name="id">A unique identifier for this <see cref="ElementReference"/>.</param>
|
||||
public ElementReference(string id) : this(id, null)
|
||||
{
|
||||
}
|
||||
|
||||
internal static ElementReference CreateWithUniqueId(ElementReferenceContext? context)
|
||||
=> new ElementReference(CreateUniqueId(), context);
|
||||
|
||||
private static string CreateUniqueId()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Context for an <see cref="ElementReference"/>.
|
||||
/// </summary>
|
||||
public abstract class ElementReferenceContext
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
|
||||
|
|
|
|||
|
|
@ -917,7 +917,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
|||
|
||||
private static void InitializeNewElementReferenceCaptureFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame)
|
||||
{
|
||||
var newElementReference = ElementReference.CreateWithUniqueId();
|
||||
var newElementReference = ElementReference.CreateWithUniqueId(diffContext.Renderer.ElementReferenceContext);
|
||||
newFrame = newFrame.WithElementReferenceCaptureId(newElementReference.Id);
|
||||
newFrame.ElementReferenceCaptureAction(newElementReference);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,6 +76,12 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
|||
/// </summary>
|
||||
public abstract Dispatcher Dispatcher { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Components.ElementReferenceContext"/> associated with this <see cref="Renderer"/>,
|
||||
/// if it exists.
|
||||
/// </summary>
|
||||
protected internal ElementReferenceContext? ElementReferenceContext { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Constructs a new component of the specified type.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -64,7 +64,8 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
_loggerFactory,
|
||||
_options,
|
||||
client,
|
||||
_loggerFactory.CreateLogger<RemoteRenderer>());
|
||||
_loggerFactory.CreateLogger<RemoteRenderer>(),
|
||||
jsRuntime.ElementReferenceContext);
|
||||
|
||||
var circuitHandlers = scope.ServiceProvider.GetServices<CircuitHandler>()
|
||||
.OrderBy(h => h.Order)
|
||||
|
|
|
|||
|
|
@ -17,12 +17,15 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
private readonly ILogger<RemoteJSRuntime> _logger;
|
||||
private CircuitClientProxy _clientProxy;
|
||||
|
||||
public ElementReferenceContext ElementReferenceContext { get; }
|
||||
|
||||
public RemoteJSRuntime(IOptions<CircuitOptions> options, ILogger<RemoteJSRuntime> logger)
|
||||
{
|
||||
_options = options.Value;
|
||||
_logger = logger;
|
||||
DefaultAsyncTimeout = _options.JSInteropDefaultCallTimeout;
|
||||
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter());
|
||||
ElementReferenceContext = new WebElementReferenceContext(this);
|
||||
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
|
||||
}
|
||||
|
||||
internal void Initialize(CircuitClientProxy clientProxy)
|
||||
|
|
|
|||
|
|
@ -37,12 +37,15 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
ILoggerFactory loggerFactory,
|
||||
CircuitOptions options,
|
||||
CircuitClientProxy client,
|
||||
ILogger logger)
|
||||
ILogger logger,
|
||||
ElementReferenceContext? elementReferenceContext)
|
||||
: base(serviceProvider, loggerFactory)
|
||||
{
|
||||
_client = client;
|
||||
_options = options;
|
||||
_logger = logger;
|
||||
|
||||
ElementReferenceContext = elementReferenceContext;
|
||||
}
|
||||
|
||||
public override Dispatcher Dispatcher { get; } = Dispatcher.CreateDefault();
|
||||
|
|
|
|||
|
|
@ -258,7 +258,13 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
private class TestRemoteRenderer : RemoteRenderer
|
||||
{
|
||||
public TestRemoteRenderer(IServiceProvider serviceProvider, IClientProxy client)
|
||||
: base(serviceProvider, NullLoggerFactory.Instance, new CircuitOptions(), new CircuitClientProxy(client, "connection"), NullLogger.Instance)
|
||||
: base(
|
||||
serviceProvider,
|
||||
NullLoggerFactory.Instance,
|
||||
new CircuitOptions(),
|
||||
new CircuitClientProxy(client, "connection"),
|
||||
NullLogger.Instance,
|
||||
null)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -437,7 +437,7 @@ namespace Microsoft.AspNetCore.Components.Web.Rendering
|
|||
private class TestRemoteRenderer : RemoteRenderer
|
||||
{
|
||||
public TestRemoteRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, CircuitOptions options, CircuitClientProxy client, ILogger logger)
|
||||
: base(serviceProvider, loggerFactory, options, client, logger)
|
||||
: base(serviceProvider, loggerFactory, options, client, logger, null)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
|||
NullLoggerFactory.Instance,
|
||||
new CircuitOptions(),
|
||||
clientProxy,
|
||||
NullLogger.Instance);
|
||||
NullLogger.Instance,
|
||||
null);
|
||||
}
|
||||
|
||||
handlers = handlers ?? Array.Empty<CircuitHandler>();
|
||||
|
|
|
|||
|
|
@ -1,22 +1,31 @@
|
|||
// 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.IO;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using Moq;
|
||||
using Xunit;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components
|
||||
{
|
||||
public class ElementReferenceJsonConverterTest
|
||||
{
|
||||
private readonly ElementReferenceJsonConverter Converter = new ElementReferenceJsonConverter();
|
||||
private readonly ElementReferenceContext ElementReferenceContext;
|
||||
private readonly ElementReferenceJsonConverter Converter;
|
||||
|
||||
public ElementReferenceJsonConverterTest()
|
||||
{
|
||||
ElementReferenceContext = Mock.Of<ElementReferenceContext>();
|
||||
Converter = new ElementReferenceJsonConverter(ElementReferenceContext);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Serializing_Works()
|
||||
{
|
||||
// Arrange
|
||||
var elementReference = ElementReference.CreateWithUniqueId();
|
||||
var elementReference = ElementReference.CreateWithUniqueId(ElementReferenceContext);
|
||||
var expected = $"{{\"__internalId\":\"{elementReference.Id}\"}}";
|
||||
var memoryStream = new MemoryStream();
|
||||
var writer = new Utf8JsonWriter(memoryStream);
|
||||
|
|
@ -34,7 +43,7 @@ namespace Microsoft.AspNetCore.Components
|
|||
public void Deserializing_Works()
|
||||
{
|
||||
// Arrange
|
||||
var id = ElementReference.CreateWithUniqueId().Id;
|
||||
var id = ElementReference.CreateWithUniqueId(ElementReferenceContext).Id;
|
||||
var json = $"{{\"__internalId\":\"{id}\"}}";
|
||||
var bytes = Encoding.UTF8.GetBytes(json);
|
||||
var reader = new Utf8JsonReader(bytes);
|
||||
|
|
@ -51,7 +60,7 @@ namespace Microsoft.AspNetCore.Components
|
|||
public void Deserializing_WithFormatting_Works()
|
||||
{
|
||||
// Arrange
|
||||
var id = ElementReference.CreateWithUniqueId().Id;
|
||||
var id = ElementReference.CreateWithUniqueId(ElementReferenceContext).Id;
|
||||
var json =
|
||||
@$"{{
|
||||
""__internalId"": ""{id}""
|
||||
|
|
|
|||
|
|
@ -11,6 +11,13 @@ namespace Microsoft.AspNetCore.Components
|
|||
{
|
||||
private static readonly JsonEncodedText IdProperty = JsonEncodedText.Encode("__internalId");
|
||||
|
||||
private readonly ElementReferenceContext _elementReferenceContext;
|
||||
|
||||
public ElementReferenceJsonConverter(ElementReferenceContext elementReferenceContext)
|
||||
{
|
||||
_elementReferenceContext = elementReferenceContext;
|
||||
}
|
||||
|
||||
public override ElementReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
string id = null;
|
||||
|
|
@ -39,7 +46,7 @@ namespace Microsoft.AspNetCore.Components
|
|||
throw new JsonException("__internalId is required.");
|
||||
}
|
||||
|
||||
return new ElementReference(id);
|
||||
return new ElementReference(id, _elementReferenceContext);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, ElementReference value, JsonSerializerOptions options)
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,13 @@
|
|||
import '@microsoft/dotnet-js-interop';
|
||||
|
||||
export const domFunctions = {
|
||||
focus,
|
||||
};
|
||||
|
||||
function focus(element: HTMLElement): void {
|
||||
if (element instanceof HTMLElement) {
|
||||
element.focus();
|
||||
} else {
|
||||
throw new Error('Unable to focus an invalid element.');
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
import { navigateTo, internalFunctions as navigationManagerInternalFunctions } from './Services/NavigationManager';
|
||||
import { attachRootComponentToElement } from './Rendering/Renderer';
|
||||
import { domFunctions } from './DomWrapper';
|
||||
|
||||
// Make the following APIs available in global scope for invocation from JS
|
||||
window['Blazor'] = {
|
||||
|
|
@ -8,5 +9,6 @@ window['Blazor'] = {
|
|||
_internal: {
|
||||
attachRootComponentToElement,
|
||||
navigationManager: navigationManagerInternalFunctions,
|
||||
domWrapper: domFunctions,
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -14,6 +14,14 @@ namespace Microsoft.AspNetCore.Components
|
|||
public string? Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
public string? ValueAttribute { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
}
|
||||
public static partial class ElementReferenceExtensions
|
||||
{
|
||||
public static System.Threading.Tasks.ValueTask FocusAsync(this Microsoft.AspNetCore.Components.ElementReference elementReference) { throw null; }
|
||||
}
|
||||
public partial class WebElementReferenceContext : Microsoft.AspNetCore.Components.ElementReferenceContext
|
||||
{
|
||||
public WebElementReferenceContext(Microsoft.JSInterop.IJSRuntime jsRuntime) { }
|
||||
}
|
||||
}
|
||||
namespace Microsoft.AspNetCore.Components.Forms
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,6 +14,14 @@ namespace Microsoft.AspNetCore.Components
|
|||
public string? Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
public string? ValueAttribute { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||
}
|
||||
public static partial class ElementReferenceExtensions
|
||||
{
|
||||
public static System.Threading.Tasks.ValueTask FocusAsync(this Microsoft.AspNetCore.Components.ElementReference elementReference) { throw null; }
|
||||
}
|
||||
public partial class WebElementReferenceContext : Microsoft.AspNetCore.Components.ElementReferenceContext
|
||||
{
|
||||
public WebElementReferenceContext(Microsoft.JSInterop.IJSRuntime jsRuntime) { }
|
||||
}
|
||||
}
|
||||
namespace Microsoft.AspNetCore.Components.Forms
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
// 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.
|
||||
|
||||
namespace Microsoft.AspNetCore.Components
|
||||
{
|
||||
internal static class DomWrapperInterop
|
||||
{
|
||||
private const string Prefix = "Blazor._internal.domWrapper.";
|
||||
|
||||
public const string Focus = Prefix + "focus";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
// 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.Threading.Tasks;
|
||||
using Microsoft.JSInterop;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components
|
||||
{
|
||||
public static class ElementReferenceExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Gives focus to an element given its <see cref="ElementReference"/>.
|
||||
/// </summary>
|
||||
/// <param name="elementReference">A reference to the element to focus.</param>
|
||||
/// <returns>The <see cref="ValueTask"/> representing the asynchronous focus operation.</returns>
|
||||
public static ValueTask FocusAsync(this ElementReference elementReference)
|
||||
{
|
||||
var jsRuntime = elementReference.GetJSRuntime();
|
||||
|
||||
if (jsRuntime == null)
|
||||
{
|
||||
throw new InvalidOperationException("No JavaScript runtime found.");
|
||||
}
|
||||
|
||||
return jsRuntime.InvokeVoidAsync(DomWrapperInterop.Focus, elementReference);
|
||||
}
|
||||
|
||||
internal static IJSRuntime GetJSRuntime(this ElementReference elementReference)
|
||||
{
|
||||
if (!(elementReference.Context is WebElementReferenceContext context))
|
||||
{
|
||||
throw new InvalidOperationException("ElementReference has not been configured correctly.");
|
||||
}
|
||||
|
||||
return context.JSRuntime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
// 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 Microsoft.JSInterop;
|
||||
|
||||
namespace Microsoft.AspNetCore.Components
|
||||
{
|
||||
public class WebElementReferenceContext : ElementReferenceContext
|
||||
{
|
||||
internal IJSRuntime JSRuntime { get; }
|
||||
|
||||
public WebElementReferenceContext(IJSRuntime jsRuntime)
|
||||
{
|
||||
JSRuntime = jsRuntime ?? throw new ArgumentNullException(nameof(jsRuntime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -188,7 +188,7 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Authentication
|
|||
var parameters = ParameterView.FromDictionary(new Dictionary<string, object>
|
||||
{
|
||||
[_action] = RemoteAuthenticationActions.LogInCallback,
|
||||
[_onLogInSucceded] = new EventCallbackFactory().Create< RemoteAuthenticationState>(
|
||||
[_onLogInSucceded] = new EventCallbackFactory().Create<RemoteAuthenticationState>(
|
||||
remoteAuthenticator,
|
||||
(state) => loggingSucceededCalled = true),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -34,6 +34,8 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering
|
|||
// The WebAssembly renderer registers and unregisters itself with the static registry
|
||||
_webAssemblyRendererId = RendererRegistry.Add(this);
|
||||
_logger = loggerFactory.CreateLogger<WebAssemblyRenderer>();
|
||||
|
||||
ElementReferenceContext = DefaultWebAssemblyJSRuntime.Instance.ElementReferenceContext;
|
||||
}
|
||||
|
||||
public override Dispatcher Dispatcher => NullDispatcher.Instance;
|
||||
|
|
|
|||
|
|
@ -10,9 +10,12 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
|
|||
{
|
||||
internal static readonly DefaultWebAssemblyJSRuntime Instance = new DefaultWebAssemblyJSRuntime();
|
||||
|
||||
public ElementReferenceContext ElementReferenceContext { get; }
|
||||
|
||||
private DefaultWebAssemblyJSRuntime()
|
||||
{
|
||||
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter());
|
||||
ElementReferenceContext = new WebElementReferenceContext(this);
|
||||
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
|
||||
}
|
||||
|
||||
#pragma warning disable IDE0051 // Remove unused private members. Invoked via Mono's JS interop mechanism (invoke_method)
|
||||
|
|
|
|||
|
|
@ -400,6 +400,26 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
|||
Browser.Equal("Clicks: 2", () => inputElement.GetAttribute("value"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanUseFocusExtensionToFocusElement()
|
||||
{
|
||||
var appElement = Browser.MountTestComponent<ElementFocusComponent>();
|
||||
var buttonElement = appElement.FindElement(By.Id("focus-button"));
|
||||
|
||||
// Make sure the input element isn't focused when the test begins; we don't want
|
||||
// the test to pass just because the input started as the focused element
|
||||
Browser.NotEqual("focus-input", getFocusedElementId);
|
||||
|
||||
// Click the button whose callback focuses the input element
|
||||
buttonElement.Click();
|
||||
|
||||
// Verify that the input element is focused
|
||||
Browser.Equal("focus-input", getFocusedElementId);
|
||||
|
||||
// A local helper that gets the ID of the focused element.
|
||||
string getFocusedElementId() => Browser.SwitchTo().ActiveElement().GetAttribute("id");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CanCaptureReferencesToDynamicallyAddedElements()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
@using Microsoft.JSInterop
|
||||
|
||||
<button id="focus-button" @onclick="FocusInput">Click to focus!</button>
|
||||
<input id="focus-input" @ref="inputReference" />
|
||||
|
||||
@code {
|
||||
private ElementReference inputReference;
|
||||
|
||||
private async Task FocusInput()
|
||||
{
|
||||
await inputReference.FocusAsync();
|
||||
}
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@
|
|||
<option value="BasicTestApp.DataDashComponent">data-* attribute rendering</option>
|
||||
<option value="BasicTestApp.DispatchingComponent">Dispatching to sync context</option>
|
||||
<option value="BasicTestApp.DuplicateAttributesComponent">Duplicate attributes</option>
|
||||
<option value="BasicTestApp.ElementFocusComponent">Element focus component</option>
|
||||
<option value="BasicTestApp.ElementRefComponent">Element ref component</option>
|
||||
<option value="BasicTestApp.ErrorComponent">Error throwing</option>
|
||||
<option value="BasicTestApp.EventBubblingComponent">Event bubbling</option>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ namespace Microsoft.AspNetCore.E2ETesting
|
|||
public static void Equal<T>(this IWebDriver driver, T expected, Func<T> actual)
|
||||
=> WaitAssertCore(driver, () => Assert.Equal(expected, actual()));
|
||||
|
||||
public static void NotEqual<T>(this IWebDriver driver, T expected, Func<T> actual)
|
||||
=> WaitAssertCore(driver, () => Assert.NotEqual(expected, actual()));
|
||||
|
||||
public static void True(this IWebDriver driver, Func<bool> actual)
|
||||
=> WaitAssertCore(driver, () => Assert.True(actual()));
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue