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 object _dummy;
|
||||||
private readonly int _dummyPrimitive;
|
private readonly int _dummyPrimitive;
|
||||||
public ElementReference(string id) { throw null; }
|
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 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)]
|
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||||
public readonly partial struct EventCallback
|
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 Renderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
|
||||||
public abstract Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get; }
|
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 { } }
|
public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } }
|
||||||
protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; }
|
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; }
|
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 object _dummy;
|
||||||
private readonly int _dummyPrimitive;
|
private readonly int _dummyPrimitive;
|
||||||
public ElementReference(string id) { throw null; }
|
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 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)]
|
[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]
|
||||||
public readonly partial struct EventCallback
|
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 Renderer(System.IServiceProvider serviceProvider, Microsoft.Extensions.Logging.ILoggerFactory loggerFactory) { }
|
||||||
public abstract Microsoft.AspNetCore.Components.Dispatcher Dispatcher { get; }
|
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 { } }
|
public event System.UnhandledExceptionEventHandler UnhandledSynchronizationException { add { } remove { } }
|
||||||
protected internal int AssignRootComponentId(Microsoft.AspNetCore.Components.IComponent component) { throw null; }
|
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; }
|
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>
|
/// </remarks>
|
||||||
public string Id { get; }
|
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;
|
Id = id;
|
||||||
|
Context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static ElementReference CreateWithUniqueId()
|
/// <summary>
|
||||||
=> new ElementReference(CreateUniqueId());
|
/// 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()
|
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>
|
<PropertyGroup>
|
||||||
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
|
<TargetFrameworks>netstandard2.0;$(DefaultNetCoreTargetFramework)</TargetFrameworks>
|
||||||
|
|
|
||||||
|
|
@ -917,7 +917,7 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
||||||
|
|
||||||
private static void InitializeNewElementReferenceCaptureFrame(ref DiffContext diffContext, ref RenderTreeFrame newFrame)
|
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 = newFrame.WithElementReferenceCaptureId(newElementReference.Id);
|
||||||
newFrame.ElementReferenceCaptureAction(newElementReference);
|
newFrame.ElementReferenceCaptureAction(newElementReference);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,6 +76,12 @@ namespace Microsoft.AspNetCore.Components.RenderTree
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract Dispatcher Dispatcher { get; }
|
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>
|
/// <summary>
|
||||||
/// Constructs a new component of the specified type.
|
/// Constructs a new component of the specified type.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,8 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
||||||
_loggerFactory,
|
_loggerFactory,
|
||||||
_options,
|
_options,
|
||||||
client,
|
client,
|
||||||
_loggerFactory.CreateLogger<RemoteRenderer>());
|
_loggerFactory.CreateLogger<RemoteRenderer>(),
|
||||||
|
jsRuntime.ElementReferenceContext);
|
||||||
|
|
||||||
var circuitHandlers = scope.ServiceProvider.GetServices<CircuitHandler>()
|
var circuitHandlers = scope.ServiceProvider.GetServices<CircuitHandler>()
|
||||||
.OrderBy(h => h.Order)
|
.OrderBy(h => h.Order)
|
||||||
|
|
|
||||||
|
|
@ -17,12 +17,15 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
||||||
private readonly ILogger<RemoteJSRuntime> _logger;
|
private readonly ILogger<RemoteJSRuntime> _logger;
|
||||||
private CircuitClientProxy _clientProxy;
|
private CircuitClientProxy _clientProxy;
|
||||||
|
|
||||||
|
public ElementReferenceContext ElementReferenceContext { get; }
|
||||||
|
|
||||||
public RemoteJSRuntime(IOptions<CircuitOptions> options, ILogger<RemoteJSRuntime> logger)
|
public RemoteJSRuntime(IOptions<CircuitOptions> options, ILogger<RemoteJSRuntime> logger)
|
||||||
{
|
{
|
||||||
_options = options.Value;
|
_options = options.Value;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
DefaultAsyncTimeout = _options.JSInteropDefaultCallTimeout;
|
DefaultAsyncTimeout = _options.JSInteropDefaultCallTimeout;
|
||||||
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter());
|
ElementReferenceContext = new WebElementReferenceContext(this);
|
||||||
|
JsonSerializerOptions.Converters.Add(new ElementReferenceJsonConverter(ElementReferenceContext));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Initialize(CircuitClientProxy clientProxy)
|
internal void Initialize(CircuitClientProxy clientProxy)
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,15 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
CircuitOptions options,
|
CircuitOptions options,
|
||||||
CircuitClientProxy client,
|
CircuitClientProxy client,
|
||||||
ILogger logger)
|
ILogger logger,
|
||||||
|
ElementReferenceContext? elementReferenceContext)
|
||||||
: base(serviceProvider, loggerFactory)
|
: base(serviceProvider, loggerFactory)
|
||||||
{
|
{
|
||||||
_client = client;
|
_client = client;
|
||||||
_options = options;
|
_options = options;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
|
||||||
|
ElementReferenceContext = elementReferenceContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Dispatcher Dispatcher { get; } = Dispatcher.CreateDefault();
|
public override Dispatcher Dispatcher { get; } = Dispatcher.CreateDefault();
|
||||||
|
|
|
||||||
|
|
@ -258,7 +258,13 @@ namespace Microsoft.AspNetCore.Components.Server.Circuits
|
||||||
private class TestRemoteRenderer : RemoteRenderer
|
private class TestRemoteRenderer : RemoteRenderer
|
||||||
{
|
{
|
||||||
public TestRemoteRenderer(IServiceProvider serviceProvider, IClientProxy client)
|
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
|
private class TestRemoteRenderer : RemoteRenderer
|
||||||
{
|
{
|
||||||
public TestRemoteRenderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory, CircuitOptions options, CircuitClientProxy client, ILogger logger)
|
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,
|
NullLoggerFactory.Instance,
|
||||||
new CircuitOptions(),
|
new CircuitOptions(),
|
||||||
clientProxy,
|
clientProxy,
|
||||||
NullLogger.Instance);
|
NullLogger.Instance,
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
handlers = handlers ?? Array.Empty<CircuitHandler>();
|
handlers = handlers ?? Array.Empty<CircuitHandler>();
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,31 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
// 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.
|
// 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.IO;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
|
using Moq;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Components
|
namespace Microsoft.AspNetCore.Components
|
||||||
{
|
{
|
||||||
public class ElementReferenceJsonConverterTest
|
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]
|
[Fact]
|
||||||
public void Serializing_Works()
|
public void Serializing_Works()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var elementReference = ElementReference.CreateWithUniqueId();
|
var elementReference = ElementReference.CreateWithUniqueId(ElementReferenceContext);
|
||||||
var expected = $"{{\"__internalId\":\"{elementReference.Id}\"}}";
|
var expected = $"{{\"__internalId\":\"{elementReference.Id}\"}}";
|
||||||
var memoryStream = new MemoryStream();
|
var memoryStream = new MemoryStream();
|
||||||
var writer = new Utf8JsonWriter(memoryStream);
|
var writer = new Utf8JsonWriter(memoryStream);
|
||||||
|
|
@ -34,7 +43,7 @@ namespace Microsoft.AspNetCore.Components
|
||||||
public void Deserializing_Works()
|
public void Deserializing_Works()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var id = ElementReference.CreateWithUniqueId().Id;
|
var id = ElementReference.CreateWithUniqueId(ElementReferenceContext).Id;
|
||||||
var json = $"{{\"__internalId\":\"{id}\"}}";
|
var json = $"{{\"__internalId\":\"{id}\"}}";
|
||||||
var bytes = Encoding.UTF8.GetBytes(json);
|
var bytes = Encoding.UTF8.GetBytes(json);
|
||||||
var reader = new Utf8JsonReader(bytes);
|
var reader = new Utf8JsonReader(bytes);
|
||||||
|
|
@ -51,7 +60,7 @@ namespace Microsoft.AspNetCore.Components
|
||||||
public void Deserializing_WithFormatting_Works()
|
public void Deserializing_WithFormatting_Works()
|
||||||
{
|
{
|
||||||
// Arrange
|
// Arrange
|
||||||
var id = ElementReference.CreateWithUniqueId().Id;
|
var id = ElementReference.CreateWithUniqueId(ElementReferenceContext).Id;
|
||||||
var json =
|
var json =
|
||||||
@$"{{
|
@$"{{
|
||||||
""__internalId"": ""{id}""
|
""__internalId"": ""{id}""
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,13 @@ namespace Microsoft.AspNetCore.Components
|
||||||
{
|
{
|
||||||
private static readonly JsonEncodedText IdProperty = JsonEncodedText.Encode("__internalId");
|
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)
|
public override ElementReference Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
{
|
{
|
||||||
string id = null;
|
string id = null;
|
||||||
|
|
@ -39,7 +46,7 @@ namespace Microsoft.AspNetCore.Components
|
||||||
throw new JsonException("__internalId is required.");
|
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)
|
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 { navigateTo, internalFunctions as navigationManagerInternalFunctions } from './Services/NavigationManager';
|
||||||
import { attachRootComponentToElement } from './Rendering/Renderer';
|
import { attachRootComponentToElement } from './Rendering/Renderer';
|
||||||
|
import { domFunctions } from './DomWrapper';
|
||||||
|
|
||||||
// Make the following APIs available in global scope for invocation from JS
|
// Make the following APIs available in global scope for invocation from JS
|
||||||
window['Blazor'] = {
|
window['Blazor'] = {
|
||||||
|
|
@ -8,5 +9,6 @@ window['Blazor'] = {
|
||||||
_internal: {
|
_internal: {
|
||||||
attachRootComponentToElement,
|
attachRootComponentToElement,
|
||||||
navigationManager: navigationManagerInternalFunctions,
|
navigationManager: navigationManagerInternalFunctions,
|
||||||
|
domWrapper: domFunctions,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,14 @@ namespace Microsoft.AspNetCore.Components
|
||||||
public string? Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
public string? Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||||
public string? ValueAttribute { [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
|
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? Type { [System.Runtime.CompilerServices.CompilerGeneratedAttribute] get { throw null; } }
|
||||||
public string? ValueAttribute { [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
|
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>
|
var parameters = ParameterView.FromDictionary(new Dictionary<string, object>
|
||||||
{
|
{
|
||||||
[_action] = RemoteAuthenticationActions.LogInCallback,
|
[_action] = RemoteAuthenticationActions.LogInCallback,
|
||||||
[_onLogInSucceded] = new EventCallbackFactory().Create< RemoteAuthenticationState>(
|
[_onLogInSucceded] = new EventCallbackFactory().Create<RemoteAuthenticationState>(
|
||||||
remoteAuthenticator,
|
remoteAuthenticator,
|
||||||
(state) => loggingSucceededCalled = true),
|
(state) => loggingSucceededCalled = true),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Rendering
|
||||||
// The WebAssembly renderer registers and unregisters itself with the static registry
|
// The WebAssembly renderer registers and unregisters itself with the static registry
|
||||||
_webAssemblyRendererId = RendererRegistry.Add(this);
|
_webAssemblyRendererId = RendererRegistry.Add(this);
|
||||||
_logger = loggerFactory.CreateLogger<WebAssemblyRenderer>();
|
_logger = loggerFactory.CreateLogger<WebAssemblyRenderer>();
|
||||||
|
|
||||||
|
ElementReferenceContext = DefaultWebAssemblyJSRuntime.Instance.ElementReferenceContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Dispatcher Dispatcher => NullDispatcher.Instance;
|
public override Dispatcher Dispatcher => NullDispatcher.Instance;
|
||||||
|
|
|
||||||
|
|
@ -10,9 +10,12 @@ namespace Microsoft.AspNetCore.Components.WebAssembly.Services
|
||||||
{
|
{
|
||||||
internal static readonly DefaultWebAssemblyJSRuntime Instance = new DefaultWebAssemblyJSRuntime();
|
internal static readonly DefaultWebAssemblyJSRuntime Instance = new DefaultWebAssemblyJSRuntime();
|
||||||
|
|
||||||
|
public ElementReferenceContext ElementReferenceContext { get; }
|
||||||
|
|
||||||
private DefaultWebAssemblyJSRuntime()
|
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)
|
#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"));
|
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]
|
[Fact]
|
||||||
public void CanCaptureReferencesToDynamicallyAddedElements()
|
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.DataDashComponent">data-* attribute rendering</option>
|
||||||
<option value="BasicTestApp.DispatchingComponent">Dispatching to sync context</option>
|
<option value="BasicTestApp.DispatchingComponent">Dispatching to sync context</option>
|
||||||
<option value="BasicTestApp.DuplicateAttributesComponent">Duplicate attributes</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.ElementRefComponent">Element ref component</option>
|
||||||
<option value="BasicTestApp.ErrorComponent">Error throwing</option>
|
<option value="BasicTestApp.ErrorComponent">Error throwing</option>
|
||||||
<option value="BasicTestApp.EventBubblingComponent">Event bubbling</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)
|
public static void Equal<T>(this IWebDriver driver, T expected, Func<T> actual)
|
||||||
=> WaitAssertCore(driver, () => Assert.Equal(expected, 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)
|
public static void True(this IWebDriver driver, Func<bool> actual)
|
||||||
=> WaitAssertCore(driver, () => Assert.True(actual()));
|
=> WaitAssertCore(driver, () => Assert.True(actual()));
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue