Repository: mui/material-ui (TypeScript)
Date: 2018-06-09 22:39:51+00:00
Task type: Bug Fix

Issue:
Can't trigger TextField onChange() when TextField is inside RadioGroup
- [x] This is a v1.x issue (v0.x is no longer maintained).
- [x ] I have searched the [issues](https://github.com/mui-org/material-ui/issues) of this repository and believe that this is not a duplicate.

## Expected Behavior
Sometimes you need to have a TextField inside RadioGroup (e.g. when a value is needed in addition to a Radio option). If user types in this TextField, the onChange event should be triggered.

## Current Behavior
Currently onChange is not triggered for any TextField inside the RadioGroup tags (suspect this may be the case for other tags as well).

## Steps to Reproduce (for bugs)

https://codesandbox.io/s/q7n3m7v1k9

Open CodeSandBox console....


Issue discussion:

Solution patch:
diff --git a/packages/material-ui/src/RadioGroup/RadioGroup.js b/packages/material-ui/src/RadioGroup/RadioGroup.js
--- a/packages/material-ui/src/RadioGroup/RadioGroup.js
+++ b/packages/material-ui/src/RadioGroup/RadioGroup.js
@@ -3,7 +3,7 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 import FormGroup from '../FormGroup';
-import { find } from '../utils/helpers';
+import { createChainedFunction, find } from '../utils/helpers';
 
 class RadioGroup extends React.Component {
   radios = [];
@@ -56,7 +56,7 @@ class RadioGroup extends React.Component {
               }
             },
             checked: value === child.props.value,
-            onChange: this.handleRadioChange,
+            onChange: createChainedFunction(child.props.onChange, this.handleRadioChange),
           });
         })}
       </FormGroup>
diff --git a/packages/material-ui/src/utils/helpers.js b/packages/material-ui/src/utils/helpers.js
--- a/packages/material-ui/src/utils/helpers.js
+++ b/packages/material-ui/src/utils/helpers.js
@@ -47,8 +47,12 @@ export function find(arr: Array<any>, pred: any) {
  * @returns {function|null}
  */
 export function createChainedFunction(...funcs: Array<any>) {
-  return funcs.filter(func => func != null).reduce(
+  return funcs.reduce(
     (acc, func) => {
+      if (func == null) {
+        return acc;
+      }
+
       warning(
         typeof func === 'function',
         'Material-UI: invalid Argument Type, must only provide functions, undefined, or null.',


Test patch:
diff --git a/packages/material-ui/src/RadioGroup/RadioGroup.test.js b/packages/material-ui/src/RadioGroup/RadioGroup.test.js
--- a/packages/material-ui/src/RadioGroup/RadioGroup.test.js
+++ b/packages/material-ui/src/RadioGroup/RadioGroup.test.js
@@ -117,35 +117,51 @@ describe('<RadioGroup />', () => {
     );
   });
 
-  describe('children radios fire change event', () => {
-    let wrapper;
-
-    beforeEach(() => {
-      wrapper = shallow(
-        <RadioGroup value="">
+  describe('prop: onChange', () => {
+    it('should fire onChange', () => {
+      const handleChange = spy();
+      const wrapper = shallow(
+        <RadioGroup value="" onChange={handleChange}>
           <Radio />
           <Radio />
         </RadioGroup>,
       );
-    });
 
-    it('should fire onChange', () => {
       const internalRadio = wrapper.children().first();
       const event = { target: { value: 'woofRadioGroup' } };
-      const onChangeSpy = spy();
-      wrapper.setProps({ onChange: onChangeSpy });
-
       internalRadio.simulate('change', event, true);
-      assert.strictEqual(onChangeSpy.callCount, 1);
-      assert.strictEqual(onChangeSpy.calledWith(event), true);
+      assert.strictEqual(handleChange.callCount, 1);
+      assert.strictEqual(handleChange.calledWith(event), true);
     });
 
     it('should not fire onChange if not checked', () => {
+      const handleChange = spy();
+      const wrapper = shallow(
+        <RadioGroup value="" onChange={handleChange}>
+          <Radio />
+          <Radio />
+        </RadioGroup>,
+      );
+
       const internalRadio = wrapper.children().first();
-      const onChangeSpy = spy();
-      wrapper.setProps({ onChange: onChangeSpy });
       internalRadio.simulate('change', { target: { value: 'woofRadioGroup' } }, false);
-      assert.strictEqual(onChangeSpy.callCount, 0);
+      assert.strictEqual(handleChange.callCount, 0);
+    });
+
+    it('should chain the onChange property', () => {
+      const handleChange1 = spy();
+      const handleChange2 = spy();
+      const wrapper = shallow(
+        <RadioGroup value="" onChange={handleChange1}>
+          <Radio onChange={handleChange2} />
+          <Radio />
+        </RadioGroup>,
+      );
+
+      const internalRadio = wrapper.children().first();
+      internalRadio.simulate('change', { target: { value: 'woofRadioGroup' } }, true);
+      assert.strictEqual(handleChange1.callCount, 1);
+      assert.strictEqual(handleChange2.callCount, 1);
     });
   });