Add tests for navigate on form submit (#24684)
* Add tests for navigate on form submit * Add fix for re-render after event dispatch * Remove deferred event handling in .NET * Only dispatch events with registered handlers once
This commit is contained in:
parent
40869f8969
commit
577f1a760f
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -2,7 +2,7 @@ import { DotNet } from '@microsoft/dotnet-js-interop';
|
||||||
import './GlobalExports';
|
import './GlobalExports';
|
||||||
import * as Environment from './Environment';
|
import * as Environment from './Environment';
|
||||||
import { monoPlatform } from './Platform/Mono/MonoPlatform';
|
import { monoPlatform } from './Platform/Mono/MonoPlatform';
|
||||||
import { renderBatch } from './Rendering/Renderer';
|
import { renderBatch, getRendererer } from './Rendering/Renderer';
|
||||||
import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch';
|
import { SharedMemoryRenderBatch } from './Rendering/RenderBatch/SharedMemoryRenderBatch';
|
||||||
import { shouldAutoStart } from './BootCommon';
|
import { shouldAutoStart } from './BootCommon';
|
||||||
import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
|
import { setEventDispatcher } from './Rendering/RendererEventDispatcher';
|
||||||
|
|
@ -26,7 +26,10 @@ async function boot(options?: Partial<WebAssemblyStartOptions>): Promise<void> {
|
||||||
// renderbatch. For example, a renderbatch might mutate the DOM in such a way as to cause an <input> to lose
|
// renderbatch. For example, a renderbatch might mutate the DOM in such a way as to cause an <input> to lose
|
||||||
// focus, in turn triggering a 'change' event. It may also be possible to listen to other DOM mutation events
|
// focus, in turn triggering a 'change' event. It may also be possible to listen to other DOM mutation events
|
||||||
// that are themselves triggered by the application of a renderbatch.
|
// that are themselves triggered by the application of a renderbatch.
|
||||||
monoPlatform.invokeWhenHeapUnlocked(() => DotNet.invokeMethodAsync('Microsoft.AspNetCore.Components.WebAssembly', 'DispatchEvent', eventDescriptor, JSON.stringify(eventArgs)));
|
const renderer = getRendererer(eventDescriptor.browserRendererId);
|
||||||
|
if (renderer.eventDelegator.getHandler(eventDescriptor.eventHandlerId)) {
|
||||||
|
monoPlatform.invokeWhenHeapUnlocked(() => DotNet.invokeMethodAsync('Microsoft.AspNetCore.Components.WebAssembly', 'DispatchEvent', eventDescriptor, JSON.stringify(eventArgs)));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Configure environment for execution under Mono WebAssembly with shared-memory rendering
|
// Configure environment for execution under Mono WebAssembly with shared-memory rendering
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ const eventPreventDefaultAttributeNamePrefix = 'preventDefault_';
|
||||||
const eventStopPropagationAttributeNamePrefix = 'stopPropagation_';
|
const eventStopPropagationAttributeNamePrefix = 'stopPropagation_';
|
||||||
|
|
||||||
export class BrowserRenderer {
|
export class BrowserRenderer {
|
||||||
private eventDelegator: EventDelegator;
|
public eventDelegator: EventDelegator;
|
||||||
|
|
||||||
private childComponentLocations: { [componentId: number]: LogicalElement } = {};
|
private childComponentLocations: { [componentId: number]: LogicalElement } = {};
|
||||||
|
|
||||||
|
|
@ -254,7 +254,7 @@ export class BrowserRenderer {
|
||||||
|
|
||||||
// this element did not have any children, so it's not inserted yet.
|
// this element did not have any children, so it's not inserted yet.
|
||||||
if (!inserted) {
|
if (!inserted) {
|
||||||
insertLogicalChild(newDomElementRaw, parent, childIndex);
|
insertLogicalChild(newDomElementRaw, parent, childIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We handle setting 'value' on a <select> in three different ways:
|
// We handle setting 'value' on a <select> in three different ways:
|
||||||
|
|
@ -265,8 +265,7 @@ export class BrowserRenderer {
|
||||||
// [3] In case the the value of the select and the option value is changed in the same batch.
|
// [3] In case the the value of the select and the option value is changed in the same batch.
|
||||||
// We just receive an attribute frame and have to set the select value afterwards.
|
// We just receive an attribute frame and have to set the select value afterwards.
|
||||||
|
|
||||||
if (newDomElementRaw instanceof HTMLOptionElement)
|
if (newDomElementRaw instanceof HTMLOptionElement) {
|
||||||
{
|
|
||||||
// Situation 1
|
// Situation 1
|
||||||
this.trySetSelectValueFromOptionElement(newDomElementRaw);
|
this.trySetSelectValueFromOptionElement(newDomElementRaw);
|
||||||
} else if (newDomElementRaw instanceof HTMLSelectElement && selectValuePropname in newDomElementRaw) {
|
} else if (newDomElementRaw instanceof HTMLSelectElement && selectValuePropname in newDomElementRaw) {
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,10 @@ export class EventDelegator {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public getHandler(eventHandlerId: number) {
|
||||||
|
return this.eventInfoStore.get(eventHandlerId);
|
||||||
|
}
|
||||||
|
|
||||||
public removeListener(eventHandlerId: number) {
|
public removeListener(eventHandlerId: number) {
|
||||||
// This method gets called whenever the .NET-side code reports that a certain event handler
|
// This method gets called whenever the .NET-side code reports that a certain event handler
|
||||||
// has been disposed. However we will already have disposed the info about that handler if
|
// has been disposed. However we will already have disposed the info about that handler if
|
||||||
|
|
@ -170,6 +174,10 @@ class EventInfoStore {
|
||||||
this.addGlobalListener(info.eventName);
|
this.addGlobalListener(info.eventName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public get(eventHandlerId: number) {
|
||||||
|
return this.infosByEventHandlerId[eventHandlerId];
|
||||||
|
}
|
||||||
|
|
||||||
public addGlobalListener(eventName: string) {
|
public addGlobalListener(eventName: string) {
|
||||||
if (this.countByEventName.hasOwnProperty(eventName)) {
|
if (this.countByEventName.hasOwnProperty(eventName)) {
|
||||||
this.countByEventName[eventName]++;
|
this.countByEventName[eventName]++;
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,10 @@ export function attachRootComponentToElement(elementSelector: string, componentI
|
||||||
attachRootComponentToLogicalElement(browserRendererId || 0, toLogicalElement(element, /* allow existing contents */ true), componentId);
|
attachRootComponentToLogicalElement(browserRendererId || 0, toLogicalElement(element, /* allow existing contents */ true), componentId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getRendererer(browserRendererId: number) {
|
||||||
|
return browserRenderers[browserRendererId];
|
||||||
|
}
|
||||||
|
|
||||||
export function renderBatch(browserRendererId: number, batch: RenderBatch): void {
|
export function renderBatch(browserRendererId: number, batch: RenderBatch): void {
|
||||||
const browserRenderer = browserRenderers[browserRendererId];
|
const browserRenderer = browserRenderers[browserRendererId];
|
||||||
if (!browserRenderer) {
|
if (!browserRenderer) {
|
||||||
|
|
|
||||||
|
|
@ -559,6 +559,18 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
||||||
Browser.Equal("", () => selectWithoutComponent.GetAttribute("value"));
|
Browser.Equal("", () => selectWithoutComponent.GetAttribute("value"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void NavigateOnSubmitWorks()
|
||||||
|
{
|
||||||
|
var app = Browser.MountTestComponent<NavigateOnSubmit>();
|
||||||
|
var input = app.FindElement(By.Id("text-input"));
|
||||||
|
|
||||||
|
input.SendKeys("Enter");
|
||||||
|
|
||||||
|
var log = Browser.Manage().Logs.GetLog(LogType.Browser);
|
||||||
|
Assert.DoesNotContain(log, entry => entry.Level == LogLevel.Severe);
|
||||||
|
}
|
||||||
|
|
||||||
private Func<string[]> CreateValidationMessagesAccessor(IWebElement appElement)
|
private Func<string[]> CreateValidationMessagesAccessor(IWebElement appElement)
|
||||||
{
|
{
|
||||||
return () => appElement.FindElements(By.ClassName("validation-message"))
|
return () => appElement.FindElements(By.ClassName("validation-message"))
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@
|
||||||
<option value="BasicTestApp.FormsTest.SimpleValidationComponentUsingExperimentalValidator">Simple validation using experimental validator</option>
|
<option value="BasicTestApp.FormsTest.SimpleValidationComponentUsingExperimentalValidator">Simple validation using experimental validator</option>
|
||||||
<option value="BasicTestApp.FormsTest.TypicalValidationComponent">Typical validation</option>
|
<option value="BasicTestApp.FormsTest.TypicalValidationComponent">Typical validation</option>
|
||||||
<option value="BasicTestApp.FormsTest.TypicalValidationComponentUsingExperimentalValidator">Typical validation using experimental validator</option>
|
<option value="BasicTestApp.FormsTest.TypicalValidationComponentUsingExperimentalValidator">Typical validation using experimental validator</option>
|
||||||
|
<option value="BasicTestApp.NavigateOnSubmit">Navigate to submit</option>
|
||||||
<option value="BasicTestApp.GlobalizationBindCases">Globalization Bind Cases</option>
|
<option value="BasicTestApp.GlobalizationBindCases">Globalization Bind Cases</option>
|
||||||
<option value="BasicTestApp.HierarchicalImportsTest.Subdir.ComponentUsingImports">Imports statement</option>
|
<option value="BasicTestApp.HierarchicalImportsTest.Subdir.ComponentUsingImports">Imports statement</option>
|
||||||
<option value="BasicTestApp.HtmlBlockChildContent">ChildContent HTML Block</option>
|
<option value="BasicTestApp.HtmlBlockChildContent">ChildContent HTML Block</option>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
@using Microsoft.AspNetCore.Components.Forms
|
||||||
|
|
||||||
|
@inject NavigationManager NavigationManager
|
||||||
|
|
||||||
|
<h3>NavigateOnSubmit</h3>
|
||||||
|
|
||||||
|
<EditForm Model="@this" OnValidSubmit="NavigateToPage">
|
||||||
|
<input @onblur="HandleBlur" id="text-input"/>
|
||||||
|
</EditForm>
|
||||||
|
|
||||||
|
@code {
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
public void NavigateToPage()
|
||||||
|
{
|
||||||
|
NavigationManager.NavigateTo("/subdir");
|
||||||
|
}
|
||||||
|
|
||||||
|
public void HandleBlur()
|
||||||
|
{
|
||||||
|
Console.WriteLine("Inside HandleBlur...");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue