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.
Nate Hunzaker committed
Mar 8, 2016 at 08:13 UTC
fe739c28830bbea3588190d7425ba9624621f6a5
7 files changed
+166
-124
src/renderers/dom/client/wrappers/DisabledInputUtils.js
new
+50
@@ -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
+2
-28
@@ -11,40 +11,14 @@
11
12
'use strict';
13
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');
15
16
/**
17
* Implements a <button> native component that does not receive mouse events
18
* when `disabled` is set.
19
*/
20
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,
22
};
23
24
module.exports = ReactDOMButton;
src/renderers/dom/client/wrappers/ReactDOMInput.js
+2
-1
@@ -11,6 +11,7 @@
11
12
'use strict';
13
14
+var DisabledInputUtils = require('DisabledInputUtils');
15
var DOMPropertyOperations = require('DOMPropertyOperations');
16
var LinkedValueUtils = require('LinkedValueUtils');
17
var ReactDOMComponentTree = require('ReactDOMComponentTree');
@@ -80,7 +81,7 @@ var ReactDOMInput = {
81
onChange: inst._wrapperState.onChange,
82
});
83
83
- return nativeProps;
84
+ return DisabledInputUtils.getNativeProps(inst, nativeProps);
85
},
86
87
mountWrapper: function(inst, props) {
src/renderers/dom/client/wrappers/ReactDOMSelect.js
+2
-1
@@ -11,6 +11,7 @@
11
12
'use strict';
13
14
+var DisabledInputUtils = require('DisabledInputUtils');
15
var LinkedValueUtils = require('LinkedValueUtils');
16
var ReactDOMComponentTree = require('ReactDOMComponentTree');
17
var ReactUpdates = require('ReactUpdates');
@@ -158,7 +159,7 @@ function updateOptions(inst, multiple, propValue) {
159
*/
160
var ReactDOMSelect = {
161
getNativeProps: function(inst, props) {
161
- return Object.assign({}, props, {
162
+ return Object.assign({}, DisabledInputUtils.getNativeProps(inst, props), {
163
onChange: inst._wrapperState.onChange,
164
value: undefined,
165
});
src/renderers/dom/client/wrappers/ReactDOMTextarea.js
+2
-1
@@ -11,6 +11,7 @@
11
12
'use strict';
13
14
+var DisabledInputUtils = require('DisabledInputUtils');
15
var DOMPropertyOperations = require('DOMPropertyOperations');
16
var LinkedValueUtils = require('LinkedValueUtils');
17
var ReactDOMComponentTree = require('ReactDOMComponentTree');
@@ -67,7 +68,7 @@ var ReactDOMTextarea = {
68
69
// Always set children to the same thing. In IE9, the selection range will
70
// get reset if `textContent` is mutated.
70
- var nativeProps = Object.assign({}, props, {
71
+ var nativeProps = Object.assign({}, DisabledInputUtils.getNativeProps(inst, props), {
72
defaultValue: undefined,
73
value: undefined,
74
children: inst._wrapperState.initialValue,
src/renderers/dom/client/wrappers/__tests__/DisabledInputUtil-test.js
new
+108
@@ -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
deleted
-93
@@ -1,93 +0,0 @@
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('ReactDOMButton', function() {
16
- var React;
17
- var ReactDOM;
18
- var ReactTestUtils;
19
-
20
- var onClick = jest.genMockFn();
21
-
22
- function expectClickThru(button) {
23
- onClick.mockClear();
24
- ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(button));
25
- expect(onClick.mock.calls.length).toBe(1);
26
- }
27
-
28
- function expectNoClickThru(button) {
29
- onClick.mockClear();
30
- ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(button));
31
- expect(onClick.mock.calls.length).toBe(0);
32
- }
33
-
34
- function mounted(button) {
35
- button = ReactTestUtils.renderIntoDocument(button);
36
- return button;
37
- }
38
-
39
- beforeEach(function() {
40
- React = require('React');
41
- ReactDOM = require('ReactDOM');
42
- ReactTestUtils = require('ReactTestUtils');
43
- });
44
-
45
- it('should forward clicks when it starts out not disabled', function() {
46
- expectClickThru(mounted(<button onClick={onClick} />));
47
- });
48
-
49
- it('should not forward clicks when it starts out disabled', function() {
50
- expectNoClickThru(
51
- mounted(<button disabled={true} onClick={onClick} />)
52
- );
53
- });
54
-
55
- it('should forward clicks when it becomes not disabled', function() {
56
- var container = document.createElement('div');
57
- var btn = ReactDOM.render(
58
- <button disabled={true} onClick={onClick} />,
59
- container
60
- );
61
- btn = ReactDOM.render(
62
- <button onClick={onClick} />,
63
- container
64
- );
65
- expectClickThru(btn);
66
- });
67
-
68
- it('should not forward clicks when it becomes disabled', function() {
69
- var container = document.createElement('div');
70
- var btn = ReactDOM.render(
71
- <button onClick={onClick} />,
72
- container
73
- );
74
- btn = ReactDOM.render(
75
- <button disabled={true} onClick={onClick} />,
76
- container
77
- );
78
- expectNoClickThru(btn);
79
- });
80
-
81
- it('should work correctly if the listener is changed', function() {
82
- var container = document.createElement('div');
83
- var btn = ReactDOM.render(
84
- <button disabled={true} onClick={function() {}} />,
85
- container
86
- );
87
- btn = ReactDOM.render(
88
- <button disabled={false} onClick={onClick} />,
89
- container
90
- );
91
- expectClickThru(btn);
92
- });
93
-});