Stopgap fix for element disabling (#8387)

Fix for #8308. This is a bad hack -- EventPluginHub.getListener isn't even DOM-specific -- but this works for now and lets us release 15.4.1.
(cherry picked from commit c7129ce1f0)
This commit is contained in:
Ben Alpert
2016-11-22 17:34:20 -08:00
committed by Ben Alpert
parent 24ffd57dfa
commit 027d8808b7
4 changed files with 77 additions and 25 deletions
@@ -156,18 +156,6 @@ function isInteractive(tag) {
);
}
function shouldPreventMouseEvent(inst) {
if (inst) {
var disabled = inst._currentElement && inst._currentElement.props.disabled;
if (disabled) {
return isInteractive(inst._tag);
}
}
return false;
}
var SimpleEventPlugin: PluginModule<MouseEvent> = {
eventTypes: eventTypes,
@@ -243,10 +231,7 @@ var SimpleEventPlugin: PluginModule<MouseEvent> = {
case 'topMouseDown':
case 'topMouseMove':
case 'topMouseUp':
// Disabled elements should not respond to mouse events
if (shouldPreventMouseEvent(targetInst)) {
return null;
}
// TODO: Disabled elements should not respond to mouse events
/* falls through */
case 'topMouseOut':
case 'topMouseOver':
@@ -17,16 +17,14 @@ describe('SimpleEventPlugin', function() {
var ReactDOM;
var ReactTestUtils;
var onClick = jest.fn();
var onClick;
function expectClickThru(element) {
onClick.mockClear();
ReactTestUtils.SimulateNative.click(ReactDOM.findDOMNode(element));
expect(onClick.mock.calls.length).toBe(1);
}
function expectNoClickThru(element) {
onClick.mockClear();
ReactTestUtils.SimulateNative.click(ReactDOM.findDOMNode(element));
expect(onClick.mock.calls.length).toBe(0);
}
@@ -40,6 +38,8 @@ describe('SimpleEventPlugin', function() {
React = require('React');
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
onClick = jest.fn();
});
it('A non-interactive tags click when disabled', function() {
@@ -53,7 +53,48 @@ describe('SimpleEventPlugin', function() {
);
var child = ReactDOM.findDOMNode(element).firstChild;
onClick.mockClear();
ReactTestUtils.SimulateNative.click(child);
expect(onClick.mock.calls.length).toBe(1);
});
it('does not register a click when clicking a child of a disabled element', function() {
var element = ReactTestUtils.renderIntoDocument(
<button onClick={onClick} disabled={true}><span /></button>
);
var child = ReactDOM.findDOMNode(element).querySelector('span');
ReactTestUtils.SimulateNative.click(child);
expect(onClick.mock.calls.length).toBe(0);
});
it('triggers click events for children of disabled elements', function() {
var element = ReactTestUtils.renderIntoDocument(
<button disabled={true}><span onClick={onClick} /></button>
);
var child = ReactDOM.findDOMNode(element).querySelector('span');
ReactTestUtils.SimulateNative.click(child);
expect(onClick.mock.calls.length).toBe(1);
});
it('triggers parent captured click events when target is a child of a disabled elements', function() {
var element = ReactTestUtils.renderIntoDocument(
<div onClickCapture={onClick}>
<button disabled={true}><span /></button>
</div>
);
var child = ReactDOM.findDOMNode(element).querySelector('span');
ReactTestUtils.SimulateNative.click(child);
expect(onClick.mock.calls.length).toBe(1);
});
it('triggers captured click events for children of disabled elements', function() {
var element = ReactTestUtils.renderIntoDocument(
<button disabled={true}><span onClickCapture={onClick} /></button>
);
var child = ReactDOM.findDOMNode(element).querySelector('span');
ReactTestUtils.SimulateNative.click(child);
expect(onClick.mock.calls.length).toBe(1);
});
@@ -124,10 +165,6 @@ describe('SimpleEventPlugin', function() {
describe('iOS bubbling click fix', function() {
// See http://www.quirksmode.org/blog/archives/2010/09/click_event_del.html
beforeEach(function() {
onClick.mockClear();
});
it('does not add a local click to interactive elements', function() {
var container = document.createElement('div');
@@ -59,6 +59,31 @@ var getDictionaryKey = function(inst) {
return '.' + inst._rootNodeID;
};
function isInteractive(tag) {
return (
tag === 'button' || tag === 'input' ||
tag === 'select' || tag === 'textarea'
);
}
function shouldPreventMouseEvent(name, type, props) {
switch (name) {
case 'onClick':
case 'onClickCapture':
case 'onDoubleClick':
case 'onDoubleClickCapture':
case 'onMouseDown':
case 'onMouseDownCapture':
case 'onMouseMove':
case 'onMouseMoveCapture':
case 'onMouseUp':
case 'onMouseUpCapture':
return !!(props.disabled && isInteractive(type));
default:
return false;
}
}
/**
* This is a unified interface for event plugins to be installed and configured.
*
@@ -133,7 +158,12 @@ var EventPluginHub = {
* @return {?function} The stored callback.
*/
getListener: function(inst, registrationName) {
// TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
// live here; needs to be moved to a better place soon
var bankForRegistrationName = listenerBank[registrationName];
if (shouldPreventMouseEvent(registrationName, inst._currentElement.type, inst._currentElement.props)) {
return null;
}
var key = getDictionaryKey(inst);
return bankForRegistrationName && bankForRegistrationName[key];
},
@@ -311,7 +311,7 @@ var CHILD_ID2 = '.0.0.1';
var idToInstance = {};
[GRANDPARENT_ID, PARENT_ID, CHILD_ID, CHILD_ID2].forEach(function(id) {
idToInstance[id] = {_rootNodeID: id};
idToInstance[id] = {_rootNodeID: id, _currentElement: {}};
});
var three = {