Fix mouse events occurring on children of a disabled form field (#16671)
* Add E2E tests, some of which fail to represent the issue * Implement handling for disableable mouse events, making the new E2E tests pass
This commit is contained in:
parent
63c802bae2
commit
0faf339143
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -21,6 +21,8 @@ const nonBubblingEvents = toLookup([
|
|||
'DOMNodeRemovedFromDocument',
|
||||
]);
|
||||
|
||||
const disableableEventNames = toLookup(['click', 'dblclick', 'mousedown', 'mousemove', 'mouseup']);
|
||||
|
||||
export interface OnEventCallback {
|
||||
(event: Event, eventHandlerId: number, eventArgs: EventForDotNet<UIEventArgs>, eventFieldInfo: EventFieldInfo | null): void;
|
||||
}
|
||||
|
|
@ -107,7 +109,7 @@ export class EventDelegator {
|
|||
const handlerInfos = this.getEventHandlerInfosForElement(candidateElement, false);
|
||||
if (handlerInfos) {
|
||||
const handlerInfo = handlerInfos.getHandler(evt.type);
|
||||
if (handlerInfo) {
|
||||
if (handlerInfo && !eventIsDisabledOnElement(candidateElement, evt.type)) {
|
||||
// We are going to raise an event for this element, so prepare info needed by the .NET code
|
||||
if (!eventArgs) {
|
||||
eventArgs = EventForDotNet.fromDOMEvent(evt);
|
||||
|
|
@ -269,3 +271,11 @@ function toLookup(items: string[]): { [key: string]: boolean } {
|
|||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function eventIsDisabledOnElement(element: Element, eventName: string): boolean {
|
||||
// We want to replicate the normal DOM event behavior that, for 'interactive' elements
|
||||
// with a 'disabled' attribute, certain mouse events are suppressed
|
||||
return (element instanceof HTMLButtonElement || element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement)
|
||||
&& disableableEventNames.hasOwnProperty(eventName)
|
||||
&& element.disabled;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -207,6 +207,37 @@ namespace Microsoft.AspNetCore.Components.E2ETest.Tests
|
|||
Browser.Equal("abcdefghijklmn", () => output.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NonInteractiveElementWithDisabledAttributeDoesRespondToMouseEvents()
|
||||
{
|
||||
Browser.MountTestComponent<EventDisablingComponent>();
|
||||
var element = Browser.FindElement(By.Id("disabled-div"));
|
||||
var eventLog = Browser.FindElement(By.Id("event-log"));
|
||||
|
||||
Browser.Equal(string.Empty, () => eventLog.GetAttribute("value"));
|
||||
element.Click();
|
||||
Browser.Equal("Got event on div", () => eventLog.GetAttribute("value"));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData("#disabled-button")]
|
||||
[InlineData("#disabled-button span")]
|
||||
[InlineData("#disabled-textarea")]
|
||||
public void InteractiveElementWithDisabledAttributeDoesNotRespondToMouseEvents(string elementSelector)
|
||||
{
|
||||
Browser.MountTestComponent<EventDisablingComponent>();
|
||||
var element = Browser.FindElement(By.CssSelector(elementSelector));
|
||||
var eventLog = Browser.FindElement(By.Id("event-log"));
|
||||
|
||||
Browser.Equal(string.Empty, () => eventLog.GetAttribute("value"));
|
||||
element.Click();
|
||||
|
||||
// It's no use observing that the log is still empty, since maybe the UI just hasn't updated yet
|
||||
// To be sure that the preceding action has no effect, we need to trigger a different action that does have an effect
|
||||
Browser.FindElement(By.Id("enabled-button")).Click();
|
||||
Browser.Equal("Got event on enabled button", () => eventLog.GetAttribute("value"));
|
||||
}
|
||||
|
||||
void SendKeysSequentially(IWebElement target, string text)
|
||||
{
|
||||
// Calling it for each character works around some chars being skipped
|
||||
|
|
|
|||
|
|
@ -0,0 +1,36 @@
|
|||
<h3 id="event-disabling">Disabling events</h3>
|
||||
|
||||
<p>
|
||||
<div @onclick="@(() => LogEvent("Got event on div"))" id="disabled-div" disabled style="border: 1px dashed red">Even though this div has a 'disabled' attribute, it still responds to mouse events, because you can't disable a div</div>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button @onclick="@(() => LogEvent("Got event on disabled button"))" id="disabled-button" disabled>Since this button is disabled, it doesn't react to mouse events <span style="border: 1px dashed green">even on its children</span></button>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<textarea @onclick="@(() => LogEvent("Got event on disabled textarea"))" id="disabled-textarea" disabled>Since this textarea is disabled, it doesn't react to mouse events</textarea>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<button @onclick="@(() => LogEvent("Got event on enabled button"))" id="enabled-button">Since this button is enabled, it does react to mouse events <span style="border: 1px dashed green">and so do its children</span></button>
|
||||
</p>
|
||||
|
||||
<h3>Event log</h3>
|
||||
|
||||
<textarea id="event-log" readonly @bind="logValue"></textarea>
|
||||
<button id="clear-log" @onclick="@(() => { logValue = string.Empty; })">Clear log</button>
|
||||
|
||||
@code {
|
||||
string logValue = string.Empty;
|
||||
|
||||
void LogEvent(string message)
|
||||
{
|
||||
if (logValue != string.Empty)
|
||||
{
|
||||
logValue += Environment.NewLine;
|
||||
}
|
||||
|
||||
logValue += message;
|
||||
}
|
||||
}
|
||||
|
|
@ -24,6 +24,7 @@
|
|||
<option value="BasicTestApp.EventBubblingComponent">Event bubbling</option>
|
||||
<option value="BasicTestApp.EventCallbackTest.EventCallbackCases">EventCallback</option>
|
||||
<option value="BasicTestApp.EventCasesComponent">Event cases</option>
|
||||
<option value="BasicTestApp.EventDisablingComponent">Event disabling</option>
|
||||
<option value="BasicTestApp.EventPreventDefaultComponent">Event preventDefault</option>
|
||||
<option value="BasicTestApp.ExternalContentPackage">External content package</option>
|
||||
<option value="BasicTestApp.FocusEventComponent">Focus events</option>
|
||||
|
|
|
|||
Loading…
Reference in New Issue