Skip to content

Commit

Permalink
Merge pull request #6215 from nhunzaker/nh-fix-disabled-inputs
Browse files Browse the repository at this point in the history
Disabled inputs should not respond to clicks in IE
  • Loading branch information
gaearon committed Apr 14, 2016
2 parents ae56910 + ec2c542 commit 36e4fe5
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 124 deletions.
50 changes: 50 additions & 0 deletions src/renderers/dom/client/wrappers/DisabledInputUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @providesModule DisabledInputUtils
*/

'use strict';

var disableableMouseListenerNames = {
onClick: true,
onDoubleClick: true,
onMouseDown: true,
onMouseMove: true,
onMouseUp: true,

onClickCapture: true,
onDoubleClickCapture: true,
onMouseDownCapture: true,
onMouseMoveCapture: true,
onMouseUpCapture: true,
};

/**
* Implements a native component that does not receive mouse events
* when `disabled` is set.
*/
var DisabledInputUtils = {
getNativeProps: function(inst, props) {
if (!props.disabled) {
return props;
}

// Copy the props, except the mouse listeners
var nativeProps = {};
for (var key in props) {
if (!disableableMouseListenerNames[key] && props.hasOwnProperty(key)) {
nativeProps[key] = props[key];
}
}

return nativeProps;
},
};

module.exports = DisabledInputUtils;
30 changes: 2 additions & 28 deletions src/renderers/dom/client/wrappers/ReactDOMButton.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,40 +11,14 @@

'use strict';

var mouseListenerNames = {
onClick: true,
onDoubleClick: true,
onMouseDown: true,
onMouseMove: true,
onMouseUp: true,

onClickCapture: true,
onDoubleClickCapture: true,
onMouseDownCapture: true,
onMouseMoveCapture: true,
onMouseUpCapture: true,
};
var DisabledInputUtils = require('DisabledInputUtils');

/**
* Implements a <button> native component that does not receive mouse events
* when `disabled` is set.
*/
var ReactDOMButton = {
getNativeProps: function(inst, props) {
if (!props.disabled) {
return props;
}

// Copy the props, except the mouse listeners
var nativeProps = {};
for (var key in props) {
if (props.hasOwnProperty(key) && !mouseListenerNames[key]) {
nativeProps[key] = props[key];
}
}

return nativeProps;
},
getNativeProps: DisabledInputUtils.getNativeProps,
};

module.exports = ReactDOMButton;
3 changes: 2 additions & 1 deletion src/renderers/dom/client/wrappers/ReactDOMInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

'use strict';

var DisabledInputUtils = require('DisabledInputUtils');
var DOMPropertyOperations = require('DOMPropertyOperations');
var LinkedValueUtils = require('LinkedValueUtils');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
Expand Down Expand Up @@ -72,7 +73,7 @@ var ReactDOMInput = {
// Make sure we set .type before any other properties (setting .value
// before .type means .value is lost in IE11 and below)
type: undefined,
}, props, {
}, DisabledInputUtils.getNativeProps(inst, props), {
defaultChecked: undefined,
defaultValue: undefined,
value: value != null ? value : inst._wrapperState.initialValue,
Expand Down
3 changes: 2 additions & 1 deletion src/renderers/dom/client/wrappers/ReactDOMSelect.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

'use strict';

var DisabledInputUtils = require('DisabledInputUtils');
var LinkedValueUtils = require('LinkedValueUtils');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactUpdates = require('ReactUpdates');
Expand Down Expand Up @@ -158,7 +159,7 @@ function updateOptions(inst, multiple, propValue) {
*/
var ReactDOMSelect = {
getNativeProps: function(inst, props) {
return Object.assign({}, props, {
return Object.assign({}, DisabledInputUtils.getNativeProps(inst, props), {
onChange: inst._wrapperState.onChange,
value: undefined,
});
Expand Down
3 changes: 2 additions & 1 deletion src/renderers/dom/client/wrappers/ReactDOMTextarea.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

'use strict';

var DisabledInputUtils = require('DisabledInputUtils');
var DOMPropertyOperations = require('DOMPropertyOperations');
var LinkedValueUtils = require('LinkedValueUtils');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
Expand Down Expand Up @@ -67,7 +68,7 @@ var ReactDOMTextarea = {

// Always set children to the same thing. In IE9, the selection range will
// get reset if `textContent` is mutated.
var nativeProps = Object.assign({}, props, {
var nativeProps = Object.assign({}, DisabledInputUtils.getNativeProps(inst, props), {
defaultValue: undefined,
value: undefined,
children: inst._wrapperState.initialValue,
Expand Down
108 changes: 108 additions & 0 deletions src/renderers/dom/client/wrappers/__tests__/DisabledInputUtil-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/**
* Copyright 2013-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
* @emails react-core
*/

'use strict';


describe('DisabledInputUtils', function() {
var React;
var ReactDOM;
var ReactTestUtils;

var elements = ['button', 'input', 'select', 'textarea'];

function expectClickThru(element) {
onClick.mockClear();
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(element));
expect(onClick.mock.calls.length).toBe(1);
}

function expectNoClickThru(element) {
onClick.mockClear();
ReactTestUtils.Simulate.click(ReactDOM.findDOMNode(element));
expect(onClick.mock.calls.length).toBe(0);
}

function mounted(element) {
element = ReactTestUtils.renderIntoDocument(element);
return element;
}

var onClick = jest.genMockFn();

elements.forEach(function(tagName) {

describe(tagName, function() {

beforeEach(function() {
React = require('React');
ReactDOM = require('ReactDOM');
ReactTestUtils = require('ReactTestUtils');
});

it('should forward clicks when it starts out not disabled', function() {
var element = React.createElement(tagName, {
onClick: onClick,
});

expectClickThru(mounted(element));
});

it('should not forward clicks when it starts out disabled', function() {
var element = React.createElement(tagName, {
onClick: onClick,
disabled: true,
});

expectNoClickThru(mounted(element));
});

it('should forward clicks when it becomes not disabled', function() {
var container = document.createElement('div');
var element = ReactDOM.render(
React.createElement(tagName, { onClick: onClick, disabled: true }),
container
);
element = ReactDOM.render(
React.createElement(tagName, { onClick: onClick }),
container
);
expectClickThru(element);
});

it('should not forward clicks when it becomes disabled', function() {
var container = document.createElement('div');
var element = ReactDOM.render(
React.createElement(tagName, { onClick: onClick }),
container
);
element = ReactDOM.render(
React.createElement(tagName, { onClick: onClick, disabled: true }),
container
);
expectNoClickThru(element);
});

it('should work correctly if the listener is changed', function() {
var container = document.createElement('div');
var element = ReactDOM.render(
React.createElement(tagName, { onClick: onClick, disabled: true }),
container
);
element = ReactDOM.render(
React.createElement(tagName, { onClick: onClick, disabled: false }),
container
);
expectClickThru(element);
});
});
});
});
93 changes: 0 additions & 93 deletions src/renderers/dom/client/wrappers/__tests__/ReactDOMButton-test.js

This file was deleted.

0 comments on commit 36e4fe5

Please sign in to comment.