Skip to content
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

Blazor InputLargeTextArea #34856

Closed
wants to merge 33 commits into from
Closed
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
48649a9
.NET to JS Streaming Interop
TanayParikh Jul 26, 2021
77ae1dc
Refactor WebView Impl
TanayParikh Jul 28, 2021
a18f82b
Update TransmitDataStreamToJS.cs
TanayParikh Jul 28, 2021
cb9c856
Blazor `InputLargeTextArea`
TanayParikh Jul 29, 2021
29aaa05
E2E Tests
TanayParikh Jul 29, 2021
fe9d5ef
Merge branch 'main' into taparik/largeTextAreaComponent
TanayParikh Jul 29, 2021
af4f4b7
Update InputLargeTextAreaTest.cs
TanayParikh Jul 29, 2021
9eef2b0
Merge branch 'main' into taparik/largeTextAreaComponent
TanayParikh Aug 3, 2021
75c4071
Merge branch 'main' into taparik/dotnetToJSStreaming
TanayParikh Aug 3, 2021
fdfffda
Update PublicAPI.Unshipped.txt
TanayParikh Aug 3, 2021
4893067
++PublicAPIs
TanayParikh Aug 3, 2021
44f71a5
Fix Build
TanayParikh Aug 3, 2021
87903e5
Unit Tests
TanayParikh Aug 3, 2021
c2af324
E2E Tests
TanayParikh Aug 3, 2021
fff1afe
Add get/set cancellation tokens
TanayParikh Aug 3, 2021
3d99404
E2E test fixes
TanayParikh Aug 4, 2021
2503d28
Remove dotNetToJSReceiveDotNetStreamReference Sync Tests
TanayParikh Aug 4, 2021
a3b90f1
Cleanup usings
TanayParikh Aug 4, 2021
6c690cd
Merge branch 'main' into taparik/dotnetToJSStreaming
TanayParikh Aug 4, 2021
588590d
Cleanup Tests
TanayParikh Aug 4, 2021
1a8bc1d
IAsyncEnumerable Based Approach
TanayParikh Aug 4, 2021
cb7b3da
Merge branch 'main' into taparik/largeTextAreaComponent
TanayParikh Aug 5, 2021
3756c06
Merge branch 'main' into taparik/dotnetToJSStreaming
TanayParikh Aug 5, 2021
9654117
Merge branch 'taparik/dotnetToJSStreaming' into taparik/largeTextArea…
TanayParikh Aug 5, 2021
39eba0e
API Update
TanayParikh Aug 5, 2021
b2f36fd
Add Test `CanGetValue_ThrowsIfTextAreaHasMoreContentThanMaxAllowed`
TanayParikh Aug 6, 2021
b380039
IAsyncEnumerable Based Approach Cleanup
TanayParikh Aug 6, 2021
0361961
Merge branch 'main' into taparik/dotnetToJSStreaming
TanayParikh Aug 6, 2021
80d884e
Update InputLargeTextAreaTest.cs
TanayParikh Aug 6, 2021
e3f8efe
Merge branch 'taparik/dotnetToJSStreaming' into taparik/largeTextArea…
TanayParikh Aug 6, 2021
03f071b
Update release js files
TanayParikh Aug 6, 2021
dc519e9
Merge branch 'main' into taparik/largeTextAreaComponent
TanayParikh Aug 6, 2021
ff7d5d6
PR Feedback
TanayParikh Aug 6, 2021
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
2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.server.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Components/Web.JS/dist/Release/blazor.webview.js

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions src/Components/Web.JS/src/GlobalExports.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Platform, Pointer, System_String, System_Array, System_Object, System_B
import { getNextChunk } from './StreamingInterop';
import { RootComponentsFunctions, enableJSRootComponents, JSComponentInfoByInitializer, JSComponentInitializerCallback } from './Rendering/JSRootComponents';
import { DotNet } from '@microsoft/dotnet-js-interop';
import { InputLargeTextArea } from './InputLargeTextArea';

interface IBlazor {
navigateTo: (uri: string, options: NavigationOptions) => void;
Expand All @@ -31,6 +32,7 @@ interface IBlazor {
PageTitle: typeof PageTitle,
forceCloseConnection?: () => Promise<void>;
InputFile?: typeof InputFile,
InputLargeTextArea?: typeof InputLargeTextArea,
invokeJSFromDotNet?: (callInfo: Pointer, arg0: any, arg1: any, arg2: any) => any;
endInvokeDotNetFromJS?: (callId: System_String, success: System_Boolean, resultJsonOrErrorMessage: System_String) => void;
receiveByteArray?: (id: System_Int, data: System_Array<System_Byte>) => void;
Expand Down Expand Up @@ -75,6 +77,7 @@ export const Blazor: IBlazor = {
Virtualize,
PageTitle,
InputFile,
InputLargeTextArea,
getJSDataStreamChunk: getNextChunk,
enableJSRootComponents,
},
Expand Down
19 changes: 19 additions & 0 deletions src/Components/Web.JS/src/InputLargeTextArea.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export const InputLargeTextArea = {
init,
getText,
setText,
};

function init(callbackWrapper: any, elem: HTMLTextAreaElement): void {
elem.addEventListener('change', function(): void {
TanayParikh marked this conversation as resolved.
Show resolved Hide resolved
callbackWrapper.invokeMethodAsync('NotifyChange', elem.value.length);
});
}

function getText(elem: HTMLTextAreaElement): string {
return elem.value;
}

function setText(elem: HTMLTextAreaElement, newValue: string): void {
elem.value = newValue;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Components.Forms
{
internal interface IInputLargeTextAreaJsCallbacks
{
Task NotifyChange(int length);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Components.Rendering;
using Microsoft.JSInterop;

namespace Microsoft.AspNetCore.Components.Forms
{
/// <summary>
/// A multiline input component for editing large <see cref="string"/> values, supports async
/// content access without binding nor validations.
TanayParikh marked this conversation as resolved.
Show resolved Hide resolved
/// </summary>
public class InputLargeTextArea : ComponentBase, IInputLargeTextAreaJsCallbacks, IDisposable
{
private ElementReference _inputLargeTextAreaElement;

private InputLargeTextAreaJsCallbacksRelay? _jsCallbacksRelay;

[Inject]
private IJSRuntime JSRuntime { get; set; } = default!;

/// <summary>
/// Gets or sets the event callback that will be invoked when the textarea content changes.
/// </summary>
[Parameter]
public EventCallback<InputLargeTextAreaChangeEventArgs> OnChange { get; set; }

/// <summary>
/// Gets or sets a collection of additional attributes that will be applied to the input element.
/// </summary>
[Parameter(CaptureUnmatchedValues = true)]
public IDictionary<string, object>? AdditionalAttributes { get; set; }

/// <summary>
/// Gets or sets the associated <see cref="ElementReference"/>.
/// <para>
/// May be <see langword="null"/> if accessed before the component is rendered.
/// </para>
/// </summary>
[DisallowNull]
public ElementReference? Element
{
get => _inputLargeTextAreaElement;
protected set => _inputLargeTextAreaElement = value!.Value;
}

/// <inheritdoc/>
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_jsCallbacksRelay = new InputLargeTextAreaJsCallbacksRelay(this);
await JSRuntime.InvokeVoidAsync(InputLargeTextAreaInterop.Init, _jsCallbacksRelay.DotNetReference, _inputLargeTextAreaElement);
}
}

/// <inheritdoc/>
protected override void BuildRenderTree(RenderTreeBuilder builder)
{
builder.OpenElement(0, "textarea");
builder.AddMultipleAttributes(1, AdditionalAttributes);
builder.AddElementReferenceCapture(2, elementReference => _inputLargeTextAreaElement = elementReference);
builder.CloseElement();
}

Task IInputLargeTextAreaJsCallbacks.NotifyChange(int length)
=> OnChange.InvokeAsync(new InputLargeTextAreaChangeEventArgs(length));

/// <summary>
/// Retrieves the textarea value asyncronously.
/// </summary>
/// <returns>The string value of the textarea.</returns>
public ValueTask<string> GetTextAsync()
TanayParikh marked this conversation as resolved.
Show resolved Hide resolved
=> JSRuntime.InvokeAsync<string>(InputLargeTextAreaInterop.GetText, _inputLargeTextAreaElement);
TanayParikh marked this conversation as resolved.
Show resolved Hide resolved

/// <summary>
/// Sets the textarea value asyncronously.
/// </summary>
/// <param name="newValue">The new content to set for the textarea.</param>
public ValueTask SetTextAsync(string newValue)
=> JSRuntime.InvokeVoidAsync(InputLargeTextAreaInterop.SetText, _inputLargeTextAreaElement, newValue);

void IDisposable.Dispose()
{
_jsCallbacksRelay?.Dispose();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;

namespace Microsoft.AspNetCore.Components.Forms
{
/// <summary>
/// Supplies information about an <see cref="InputLargeTextArea.OnChange"/> event being raised.
/// </summary>
public sealed class InputLargeTextAreaChangeEventArgs : EventArgs
{
/// <summary>
/// Constructs a new <see cref="InputLargeTextAreaChangeEventArgs"/> instance.
/// </summary>
/// <param name="length">The length of the textarea value.</param>
public InputLargeTextAreaChangeEventArgs(int length)
{
Length = length;
}

/// <summary>
/// Gets the length of the textarea value.
/// </summary>
public int Length { get; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the type be long? Can it be > 2GB? I know that it would be pretty outrageous to have a 2GB textarea, but do we definitely want to preclude it?

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// 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.Forms
{
internal static class InputLargeTextAreaInterop
TanayParikh marked this conversation as resolved.
Show resolved Hide resolved
{
private const string JsFunctionsPrefix = "Blazor._internal.InputLargeTextArea.";

public const string Init = JsFunctionsPrefix + "init";

public const string GetText = JsFunctionsPrefix + "getText";

public const string SetText = JsFunctionsPrefix + "setText";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using Microsoft.JSInterop;

namespace Microsoft.AspNetCore.Components.Forms
{
internal class InputLargeTextAreaJsCallbacksRelay : IDisposable
TanayParikh marked this conversation as resolved.
Show resolved Hide resolved
{
private readonly IInputLargeTextAreaJsCallbacks _callbacks;

public IDisposable DotNetReference { get; }

[DynamicDependency(nameof(NotifyChange))]
public InputLargeTextAreaJsCallbacksRelay(IInputLargeTextAreaJsCallbacks callbacks)
{
_callbacks = callbacks;

DotNetReference = DotNetObjectReference.Create(this);
}

[JSInvokable]
public Task NotifyChange(int length)
=> _callbacks.NotifyChange(length);

public void Dispose()
{
DotNetReference.Dispose();
}
}
}
15 changes: 15 additions & 0 deletions src/Components/Web/src/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@ Microsoft.AspNetCore.Components.Forms.InputDateType.Month = 2 -> Microsoft.AspNe
Microsoft.AspNetCore.Components.Forms.InputDateType.Time = 3 -> Microsoft.AspNetCore.Components.Forms.InputDateType
Microsoft.AspNetCore.Components.Forms.InputFile.Element.get -> Microsoft.AspNetCore.Components.ElementReference?
Microsoft.AspNetCore.Components.Forms.InputFile.Element.set -> void
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.AdditionalAttributes.get -> System.Collections.Generic.IDictionary<string!, object!>?
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.AdditionalAttributes.set -> void
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.Element.get -> Microsoft.AspNetCore.Components.ElementReference?
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.Element.set -> void
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.GetTextAsync() -> System.Threading.Tasks.ValueTask<string!>
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.InputLargeTextArea() -> void
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.OnChange.get -> Microsoft.AspNetCore.Components.EventCallback<Microsoft.AspNetCore.Components.Forms.InputLargeTextAreaChangeEventArgs!>
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.OnChange.set -> void
Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.SetTextAsync(string! newValue) -> System.Threading.Tasks.ValueTask
Microsoft.AspNetCore.Components.Forms.InputLargeTextAreaChangeEventArgs
Microsoft.AspNetCore.Components.Forms.InputLargeTextAreaChangeEventArgs.InputLargeTextAreaChangeEventArgs(int length) -> void
Microsoft.AspNetCore.Components.Forms.InputLargeTextAreaChangeEventArgs.Length.get -> int
Microsoft.AspNetCore.Components.Forms.InputNumber<TValue>.Element.get -> Microsoft.AspNetCore.Components.ElementReference?
Microsoft.AspNetCore.Components.Forms.InputNumber<TValue>.Element.set -> void
Microsoft.AspNetCore.Components.Forms.InputSelect<TValue>.Element.get -> Microsoft.AspNetCore.Components.ElementReference?
Expand Down Expand Up @@ -60,6 +73,8 @@ Microsoft.AspNetCore.Components.Web.PageTitle.ChildContent.set -> void
Microsoft.AspNetCore.Components.Web.PageTitle.PageTitle() -> void
override Microsoft.AspNetCore.Components.Forms.InputDate<TValue>.OnParametersSet() -> void
abstract Microsoft.AspNetCore.Components.RenderTree.WebRenderer.AttachRootComponentToBrowser(int componentId, string! domElementSelector) -> void
override Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void
override Microsoft.AspNetCore.Components.Forms.InputLargeTextArea.OnAfterRenderAsync(bool firstRender) -> System.Threading.Tasks.Task!
override Microsoft.AspNetCore.Components.Routing.FocusOnNavigate.OnAfterRenderAsync(bool firstRender) -> System.Threading.Tasks.Task!
override Microsoft.AspNetCore.Components.Routing.FocusOnNavigate.OnParametersSet() -> void
override Microsoft.AspNetCore.Components.Web.ErrorBoundary.BuildRenderTree(Microsoft.AspNetCore.Components.Rendering.RenderTreeBuilder! builder) -> void
Expand Down
Loading