Don't trigger mouse events on native button elements that are disabled
This adds a `ReactDOMButton` module that shims the native `<button>` React component so it doesn't receive mouseup, mousemove, mousedown, click, or double-click events when its disabled property is truthy.
Andrew Zich committed
Aug 29, 2013 at 17:31 UTC
7d34c09e171232a152f76cde0530f1701e57d694
3 files changed
+154
src/core/ReactDefaultInjection.js
+2
@@ -19,6 +19,7 @@
19
"use strict";
20
21
var ReactDOM = require('ReactDOM');
22
+var ReactDOMButton = require('ReactDOMButton');
23
var ReactDOMForm = require('ReactDOMForm');
24
var ReactDOMInput = require('ReactDOMInput');
25
var ReactDOMOption = require('ReactDOMOption');
@@ -59,6 +60,7 @@ function inject() {
60
});
61
62
ReactDOM.injection.injectComponentClasses({
63
+ button: ReactDOMButton,
64
form: ReactDOMForm,
65
input: ReactDOMInput,
66
option: ReactDOMOption,
src/dom/components/ReactDOMButton.js
new
+64
@@ -0,0 +1,64 @@
1
+/**
2
+ * Copyright 2013 Facebook, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ * @providesModule ReactDOMButton
17
+ */
18
+
19
+"use strict";
20
+
21
+var ReactCompositeComponent = require('ReactCompositeComponent');
22
+var ReactDOM = require('ReactDOM');
23
+
24
+var keyMirror = require('keyMirror');
25
+
26
+// Store a reference to the <button> `ReactNativeComponent`.
27
+var button = ReactDOM.button;
28
+
29
+var mouseListenerNames = keyMirror({
30
+ onClick: true,
31
+ onDoubleClick: true,
32
+ onMouseDown: true,
33
+ onMouseMove: true,
34
+ onMouseUp: true,
35
+ onClickCapture: true,
36
+ onDoubleClickCapture: true,
37
+ onMouseDownCapture: true,
38
+ onMouseMoveCapture: true,
39
+ onMouseUpCapture: true
40
+});
41
+
42
+/**
43
+ * Implements a <button> native component that does not receive mouse events
44
+ * when `disabled` is set.
45
+ */
46
+var ReactDOMButton = ReactCompositeComponent.createClass({
47
+
48
+ render: function() {
49
+ var props = {};
50
+
51
+ // Copy the props; except the mouse listeners if we're disabled
52
+ for (var key in this.props) {
53
+ if (this.props.hasOwnProperty(key) &&
54
+ (!this.props.disabled || !mouseListenerNames[key])) {
55
+ props[key] = this.props[key];
56
+ }
57
+ }
58
+
59
+ return button(props, this.props.children);
60
+ }
61
+
62
+});
63
+
64
+module.exports = ReactDOMButton;
src/dom/components/__tests__/ReactDOMButton-test.js
new
+88
@@ -0,0 +1,88 @@
1
+/**
2
+ * Copyright 2013 Facebook, Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ * @jsx React.DOM
17
+ * @emails react-core
18
+ */
19
+
20
+"use strict";
21
+
22
+/*jshint evil:true */
23
+
24
+var mocks = require('mocks');
25
+
26
+describe('ReactDOMButton', function() {
27
+ var React;
28
+ var ReactTestUtils;
29
+
30
+ var onClick = mocks.getMockFunction();
31
+
32
+ function expectClickThru(button) {
33
+ onClick.mockClear();
34
+ ReactTestUtils.Simulate.click(button.getDOMNode());
35
+ expect(onClick.mock.calls.length).toBe(1);
36
+ }
37
+
38
+ function expectNoClickThru(button) {
39
+ onClick.mockClear();
40
+ ReactTestUtils.Simulate.click(button.getDOMNode());
41
+ expect(onClick.mock.calls.length).toBe(0);
42
+ }
43
+
44
+ function mounted(button) {
45
+ ReactTestUtils.renderIntoDocument(button);
46
+ return button;
47
+ }
48
+
49
+ beforeEach(function() {
50
+ React = require('React');
51
+ ReactTestUtils = require('ReactTestUtils');
52
+ });
53
+
54
+ it('should forward clicks when it starts out not disabled', function() {
55
+ expectClickThru(mounted(<button onClick={onClick} />));
56
+ });
57
+
58
+ it('should not forward clicks when it starts out disabled', function() {
59
+ expectNoClickThru(
60
+ mounted(<button disabled={true} onClick={onClick} />)
61
+ );
62
+ });
63
+
64
+ it('should forward clicks when it becomes not disabled', function() {
65
+ var btn = mounted(<button disabled={true} onClick={onClick} />);
66
+ btn.setProps({disabled: false});
67
+ expectClickThru(btn);
68
+ });
69
+
70
+ it('should not forward clicks when it becomes disabled', function() {
71
+ var btn = mounted(<button onClick={onClick} />);
72
+ btn.setProps({disabled: true});
73
+ expectNoClickThru(btn);
74
+ });
75
+
76
+ it('should work correctly if the listener is changed', function() {
77
+ var btn = mounted(
78
+ <button disabled={true} onClick={function() {}} />
79
+ );
80
+
81
+ btn.setProps({
82
+ disabled: false,
83
+ onClick: onClick
84
+ });
85
+
86
+ expectClickThru(btn);
87
+ });
88
+});