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:
Steve Sanderson 2019-10-31 09:29:15 +00:00 committed by GitHub
parent 63c802bae2
commit 0faf339143
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 82 additions and 4 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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>