@samitouri / QOS-React-2 / commits / 7d34c09e17

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 +});