Skip to content

Commit 03b4c59

Browse files
committed
Disabled inputs should not respond to clicks in IE
This commit migrates over the disabled property behavior from ReactDOMButton into a general purpose disabled event filter. It also applies that behavior to inputs, selects, and textareas.
1 parent f646357 commit 03b4c59

File tree

7 files changed

+166
-124
lines changed

7 files changed

+166
-124
lines changed
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright 2013-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @providesModule DisabledInputUtils
10+
*/
11+
12+
'use strict';
13+
14+
var disableableMouseListenerNames = {
15+
onClick: true,
16+
onDoubleClick: true,
17+
onMouseDown: true,
18+
onMouseMove: true,
19+
onMouseUp: true,
20+
21+
onClickCapture: true,
22+
onDoubleClickCapture: true,
23+
onMouseDownCapture: true,
24+
onMouseMoveCapture: true,
25+
onMouseUpCapture: true,
26+
};
27+
28+
/**
29+
* Implements a native component that does not receive mouse events
30+
* when `disabled` is set.
31+
*/
32+
var DisabledInputUtils = {
33+
getNativeProps: function(inst, props) {
34+
if (!props.disabled) {
35+
return props;
36+
}
37+
38+
// Copy the props, except the mouse listeners
39+
var nativeProps = {};
40+
for (var key in props) {
41+
if (props.hasOwnProperty(key) && !disableableMouseListenerNames[key]) {
42+
nativeProps[key] = props[key];
43+
}
44+
}
45+
46+
return nativeProps;
47+
},
48+
};
49+
50+
module.exports = DisabledInputUtils;

src/renderers/dom/client/wrappers/ReactDOMButton.js

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -11,40 +11,14 @@
1111

1212
'use strict';
1313

14-
var mouseListenerNames = {
15-
onClick: true,
16-
onDoubleClick: true,
17-
onMouseDown: true,
18-
onMouseMove: true,
19-
onMouseUp: true,
20-
21-
onClickCapture: true,
22-
onDoubleClickCapture: true,
23-
onMouseDownCapture: true,
24-
onMouseMoveCapture: true,
25-
onMouseUpCapture: true,
26-
};
14+
var DisabledInputUtils = require('DisabledInputUtils');
2715

2816
/**
2917
* Implements a <button> native component that does not receive mouse events
3018
* when `disabled` is set.
3119
*/
3220
var ReactDOMButton = {
33-
getNativeProps: function(inst, props) {
34-
if (!props.disabled) {
35-
return props;
36-
}
37-
38-
// Copy the props, except the mouse listeners
39-
var nativeProps = {};
40-
for (var key in props) {
41-
if (props.hasOwnProperty(key) && !mouseListenerNames[key]) {
42-
nativeProps[key] = props[key];
43-
}
44-
}
45-
46-
return nativeProps;
47-
},
21+
getNativeProps: DisabledInputUtils.getNativeProps,
4822
};
4923

5024
module.exports = ReactDOMButton;

src/renderers/dom/client/wrappers/ReactDOMInput.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
'use strict';
1313

14+
var DisabledInputUtils = require('DisabledInputUtils');
1415
var DOMPropertyOperations = require('DOMPropertyOperations');
1516
var LinkedValueUtils = require('LinkedValueUtils');
1617
var ReactDOMComponentTree = require('ReactDOMComponentTree');
@@ -81,7 +82,7 @@ var ReactDOMInput = {
8182
onChange: inst._wrapperState.onChange,
8283
});
8384

84-
return nativeProps;
85+
return DisabledInputUtils.getNativeProps(inst, nativeProps);
8586
},
8687

8788
mountWrapper: function(inst, props) {

src/renderers/dom/client/wrappers/ReactDOMSelect.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
'use strict';
1313

14+
var DisabledInputUtils = require('DisabledInputUtils');
1415
var LinkedValueUtils = require('LinkedValueUtils');
1516
var ReactDOMComponentTree = require('ReactDOMComponentTree');
1617
var ReactUpdates = require('ReactUpdates');
@@ -159,7 +160,7 @@ function updateOptions(inst, multiple, propValue) {
159160
*/
160161
var ReactDOMSelect = {
161162
getNativeProps: function(inst, props) {
162-
return assign({}, props, {
163+
return assign({}, DisabledInputUtils.getNativeProps(inst, props), {
163164
onChange: inst._wrapperState.onChange,
164165
value: undefined,
165166
});

src/renderers/dom/client/wrappers/ReactDOMTextarea.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
'use strict';
1313

14+
var DisabledInputUtils = require('DisabledInputUtils');
1415
var DOMPropertyOperations = require('DOMPropertyOperations');
1516
var LinkedValueUtils = require('LinkedValueUtils');
1617
var ReactDOMComponentTree = require('ReactDOMComponentTree');
@@ -68,7 +69,7 @@ var ReactDOMTextarea = {
6869

6970
// Always set children to the same thing. In IE9, the selection range will
7071
// get reset if `textContent` is mutated.
71-
var nativeProps = assign({}, props, {
72+
var nativeProps = assign({}, DisabledInputUtils.getNativeProps(inst, props), {
7273
defaultValue: undefined,
7374
value: undefined,
7475
children: inst._wrapperState.initialValue,
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* Copyright 2013-present, Facebook, Inc.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*
9+
* @emails react-core
10+
*/
11+
12+
'use strict';
13+
14+
15+
describe('DisabledInputUtils', function() {
16+
var React;
17+
var ReactDOM;
18+
var ReactTestUtils;
19+
20+
var elements = ['button', 'input', 'select', 'textarea'];
21+
22+
function expectClickThru(element) {
23+
onClick.mockClear();
24+
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(element));
25+
expect(onClick.mock.calls.length).toBe(1);
26+
}
27+
28+
function expectNoClickThru(element) {
29+
onClick.mockClear();
30+
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(element));
31+
expect(onClick.mock.calls.length).toBe(0);
32+
}
33+
34+
function mounted(element) {
35+
element = ReactTestUtils.renderIntoDocument(element);
36+
return element;
37+
}
38+
39+
var onClick = jest.genMockFn();
40+
41+
elements.forEach(function(tagName) {
42+
43+
describe(tagName, function() {
44+
45+
beforeEach(function() {
46+
React = require('React');
47+
ReactDOM = require('ReactDOM');
48+
ReactTestUtils = require('ReactTestUtils');
49+
});
50+
51+
it('should forward clicks when it starts out not disabled', function() {
52+
var element = React.createElement(tagName, {
53+
onClick: onClick,
54+
});
55+
56+
expectClickThru(mounted(element));
57+
});
58+
59+
it('should not forward clicks when it starts out disabled', function() {
60+
var element = React.createElement(tagName, {
61+
onClick: onClick,
62+
disabled: true,
63+
});
64+
65+
expectNoClickThru(mounted(element));
66+
});
67+
68+
it('should forward clicks when it becomes not disabled', function() {
69+
var container = document.createElement('div');
70+
var element = ReactDOM.render(
71+
React.createElement(tagName, { onClick: onClick, disabled: true }),
72+
container
73+
);
74+
element = ReactDOM.render(
75+
React.createElement(tagName, { onClick: onClick }),
76+
container
77+
);
78+
expectClickThru(element);
79+
});
80+
81+
it('should not forward clicks when it becomes disabled', function() {
82+
var container = document.createElement('div');
83+
var element = ReactDOM.render(
84+
React.createElement(tagName, { onClick: onClick }),
85+
container
86+
);
87+
element = ReactDOM.render(
88+
React.createElement(tagName, { onClick: onClick, disabled: true }),
89+
container
90+
);
91+
expectNoClickThru(element);
92+
});
93+
94+
it('should work correctly if the listener is changed', function() {
95+
var container = document.createElement('div');
96+
var element = ReactDOM.render(
97+
React.createElement(tagName, { onClick: onClick, disabled: true }),
98+
container
99+
);
100+
element = ReactDOM.render(
101+
React.createElement(tagName, { onClick: onClick, disabled: false }),
102+
container
103+
);
104+
expectClickThru(element);
105+
});
106+
});
107+
});
108+
});

src/renderers/dom/client/wrappers/__tests__/ReactDOMButton-test.js

Lines changed: 0 additions & 93 deletions
This file was deleted.

0 commit comments

Comments
 (0)