Skip to content

Commit

Permalink
Stopgap fix for element disabling (#8387)
Browse files Browse the repository at this point in the history
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 c7129ce)
  • Loading branch information
sophiebits committed Nov 23, 2016
1 parent 24ffd57 commit 027d880
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 25 deletions.
17 changes: 1 addition & 16 deletions src/renderers/dom/client/eventPlugins/SimpleEventPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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':
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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() {
Expand All @@ -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);
});
Expand Down Expand Up @@ -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');

Expand Down
30 changes: 30 additions & 0 deletions src/renderers/shared/stack/event/EventPluginHub.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*
Expand Down Expand Up @@ -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];
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down

0 comments on commit 027d880

Please sign in to comment.