diff --git a/packages/material-ui/src/Radio/Radio.js b/packages/material-ui/src/Radio/Radio.js
index c78d02649442b8..096261d8b2037f 100644
--- a/packages/material-ui/src/Radio/Radio.js
+++ b/packages/material-ui/src/Radio/Radio.js
@@ -4,8 +4,9 @@ import clsx from 'clsx';
import SwitchBase from '../internal/SwitchBase';
import RadioButtonUncheckedIcon from '../internal/svg-icons/RadioButtonUnchecked';
import RadioButtonCheckedIcon from '../internal/svg-icons/RadioButtonChecked';
-import { capitalize } from '../utils/helpers';
+import { capitalize, createChainedFunction } from '../utils/helpers';
import withStyles from '../styles/withStyles';
+import RadioGroupContext from '../RadioGroup/RadioGroupContext';
export const styles = theme => ({
/* Styles applied to the root element. */
@@ -37,7 +38,28 @@ export const styles = theme => ({
});
const Radio = React.forwardRef(function Radio(props, ref) {
- const { classes, color, ...other } = props;
+ const {
+ checked: checkedProp,
+ classes,
+ color,
+ name: nameProp,
+ onChange: onChangeProp,
+ ...other
+ } = props;
+ const radioGroup = React.useContext(RadioGroupContext);
+
+ let checked = checkedProp;
+ const onChange = createChainedFunction(onChangeProp, radioGroup && radioGroup.onChange);
+ let name = nameProp;
+
+ if (radioGroup) {
+ if (typeof checked === 'undefined') {
+ checked = radioGroup.value === props.value;
+ }
+ if (typeof name === 'undefined') {
+ name = radioGroup.name;
+ }
+ }
return (
@@ -97,6 +122,10 @@ Radio.propTypes = {
* Use that property to pass a ref callback to the native input component.
*/
inputRef: PropTypes.oneOfType([PropTypes.func, PropTypes.object]),
+ /**
+ * Name attribute of the `input` element.
+ */
+ name: PropTypes.string,
/**
* Callback fired when the state is changed.
*
diff --git a/packages/material-ui/src/Radio/Radio.test.js b/packages/material-ui/src/Radio/Radio.test.js
index 1aaca1e7d2ed27..3644212be11b5a 100644
--- a/packages/material-ui/src/Radio/Radio.test.js
+++ b/packages/material-ui/src/Radio/Radio.test.js
@@ -2,22 +2,14 @@ import React from 'react';
import { assert } from 'chai';
import RadioButtonCheckedIcon from '../internal/svg-icons/RadioButtonChecked';
import RadioButtonUncheckedIcon from '../internal/svg-icons/RadioButtonUnchecked';
-import {
- getClasses,
- createShallow,
- createMount,
- describeConformance,
-} from '@material-ui/core/test-utils';
-import SwitchBase from '../internal/SwitchBase';
+import { getClasses, createMount, describeConformance } from '@material-ui/core/test-utils';
import Radio from './Radio';
describe('', () => {
- let shallow;
let classes;
let mount;
before(() => {
- shallow = createShallow({ dive: true });
classes = getClasses();
mount = createMount();
});
@@ -42,11 +34,6 @@ describe('', () => {
});
});
- it('should be using SwitchBase', () => {
- const wrapper = shallow();
- assert.strictEqual(wrapper.type(), SwitchBase);
- });
-
describe('prop: unchecked', () => {
it('should render an unchecked icon', () => {
const wrapper = mount();
diff --git a/packages/material-ui/src/RadioGroup/RadioGroup.js b/packages/material-ui/src/RadioGroup/RadioGroup.js
index 94c8be29c07abf..30228cf0385876 100644
--- a/packages/material-ui/src/RadioGroup/RadioGroup.js
+++ b/packages/material-ui/src/RadioGroup/RadioGroup.js
@@ -4,40 +4,31 @@ import React from 'react';
import PropTypes from 'prop-types';
import warning from 'warning';
import FormGroup from '../FormGroup';
-import { createChainedFunction, find } from '../utils/helpers';
+import { setRef } from '../utils/reactHelpers';
+import RadioGroupContext from './RadioGroupContext';
const RadioGroup = React.forwardRef(function RadioGroup(props, ref) {
- const { actions, children, defaultValue, name, value: valueProp, onChange, ...other } = props;
- const radiosRef = React.useRef([]);
+ const { actions, children, name, value: valueProp, onChange, ...other } = props;
+ const rootRef = React.useRef();
const { current: isControlled } = React.useRef(props.value != null);
const [valueState, setValue] = React.useState(() => {
if (!isControlled) {
- return defaultValue;
+ return props.defaultValue;
}
return null;
});
React.useImperativeHandle(actions, () => ({
focus: () => {
- const radios = radiosRef.current;
- if (!radios.length) {
- return;
- }
-
- const focusRadios = radios.filter(n => !n.disabled);
+ let input = rootRef.current.querySelector('input:not(:disabled):checked');
- if (!focusRadios.length) {
- return;
+ if (!input) {
+ input = rootRef.current.querySelector('input:not(:disabled)');
}
- const selectedRadio = find(focusRadios, n => n.checked);
-
- if (selectedRadio) {
- selectedRadio.focus();
- return;
+ if (input) {
+ input.focus();
}
-
- focusRadios[0].focus();
},
}));
@@ -67,34 +58,18 @@ const RadioGroup = React.forwardRef(function RadioGroup(props, ref) {
onChange(event, event.target.value);
}
};
+ const context = { name, onChange: handleChange, value };
- radiosRef.current = [];
return (
-
- {React.Children.map(children, child => {
- if (!React.isValidElement(child)) {
- return null;
- }
-
- warning(
- child.type !== React.Fragment,
- [
- "Material-UI: the RadioGroup component doesn't accept a Fragment as a child.",
- 'Consider providing an array instead.',
- ].join('\n'),
- );
-
- return React.cloneElement(child, {
- name,
- inputRef: node => {
- if (node) {
- radiosRef.current.push(node);
- }
- },
- checked: value === child.props.value,
- onChange: createChainedFunction(child.props.onChange, handleChange),
- });
- })}
+ {
+ setRef(ref, nodeRef);
+ setRef(rootRef, nodeRef);
+ }}
+ {...other}
+ >
+ {children}
);
});
diff --git a/packages/material-ui/src/RadioGroup/RadioGroup.test.js b/packages/material-ui/src/RadioGroup/RadioGroup.test.js
index 36af95fa928ab4..e9725168426869 100644
--- a/packages/material-ui/src/RadioGroup/RadioGroup.test.js
+++ b/packages/material-ui/src/RadioGroup/RadioGroup.test.js
@@ -117,49 +117,35 @@ describe('', () => {
it('should focus the selected radio', () => {
const actionsRef = React.createRef();
- const zeroRadioOnFocus = spy();
- const oneRadioOnFocus = spy();
const twoRadioOnFocus = spy();
- const threeRadioOnFocus = spy();
mount(
-
-
+
+
-
+
,
);
actionsRef.current.focus();
-
- assert.strictEqual(zeroRadioOnFocus.callCount, 0);
- assert.strictEqual(oneRadioOnFocus.callCount, 0);
assert.strictEqual(twoRadioOnFocus.callCount, 1);
- assert.strictEqual(threeRadioOnFocus.callCount, 0);
});
it('should focus the non-disabled radio rather than the disabled selected radio', () => {
const actionsRef = React.createRef();
- const zeroRadioOnFocus = spy();
- const oneRadioOnFocus = spy();
- const twoRadioOnFocus = spy();
const threeRadioOnFocus = spy();
mount(
-
-
-
+
+
+
,
);
actionsRef.current.focus();
-
- assert.strictEqual(zeroRadioOnFocus.callCount, 0);
- assert.strictEqual(oneRadioOnFocus.callCount, 0);
- assert.strictEqual(twoRadioOnFocus.callCount, 0);
assert.strictEqual(threeRadioOnFocus.callCount, 1);
});
diff --git a/packages/material-ui/src/RadioGroup/RadioGroupContext.js b/packages/material-ui/src/RadioGroup/RadioGroupContext.js
new file mode 100644
index 00000000000000..22f5760984d9db
--- /dev/null
+++ b/packages/material-ui/src/RadioGroup/RadioGroupContext.js
@@ -0,0 +1,8 @@
+import React from 'react';
+
+/**
+ * @ignore - internal component.
+ */
+const RadioGroupContext = React.createContext();
+
+export default RadioGroupContext;
diff --git a/packages/material-ui/src/utils/helpers.js b/packages/material-ui/src/utils/helpers.js
index 65ca07fed7d061..6b9ae1fe4bc34c 100644
--- a/packages/material-ui/src/utils/helpers.js
+++ b/packages/material-ui/src/utils/helpers.js
@@ -32,11 +32,6 @@ export function findIndex(arr, pred) {
return -1;
}
-export function find(arr, pred) {
- const index = findIndex(arr, pred);
- return index > -1 ? arr[index] : undefined;
-}
-
/**
* Safe chained function
*
diff --git a/packages/material-ui/src/utils/helpers.test.js b/packages/material-ui/src/utils/helpers.test.js
index fe1f9e2a8f3118..ff01647c9c6ecc 100644
--- a/packages/material-ui/src/utils/helpers.test.js
+++ b/packages/material-ui/src/utils/helpers.test.js
@@ -1,5 +1,5 @@
import { assert } from 'chai';
-import { capitalize, contains, find } from './helpers';
+import { capitalize, contains } from './helpers';
describe('utils/helpers.js', () => {
describe('capitalize', () => {
@@ -14,16 +14,6 @@ describe('utils/helpers.js', () => {
});
});
- describe('find(arr, pred)', () => {
- it('should search for an item in an array containing the predicate', () => {
- const array = ['woofHelpers', 'meow', { foo: 'bar' }, { woofHelpers: 'meow' }];
- assert.strictEqual(find(array, 'lol'), undefined);
- assert.strictEqual(find(array, 'woofHelpers'), array[0]);
- assert.strictEqual(find(array, { foo: 'bar' }), array[2]);
- assert.strictEqual(find(array, n => n && n.woofHelpers === 'meow'), array[3]);
- });
- });
-
describe('contains(obj, pred)', () => {
it('should check if an object contains the partial object', () => {
const obj = { woofHelpers: 'meow', cat: 'dog' };
diff --git a/pages/api/radio.md b/pages/api/radio.md
index b314778cfb5fcc..0549f70e6306bf 100644
--- a/pages/api/radio.md
+++ b/pages/api/radio.md
@@ -28,6 +28,7 @@ import Radio from '@material-ui/core/Radio';
| id | string | | The id of the `input` element. |
| inputProps | object | | Attributes applied to the `input` element. |
| inputRef | union: func |
object
| | Use that property to pass a ref callback to the native input component. |
+| name | string | | Name attribute of the `input` element. |
| onChange | func | | Callback fired when the state is changed.
**Signature:**
`function(event: object, checked: boolean) => void`
*event:* The event source of the callback. You can pull out the new value by accessing `event.target.value`.
*checked:* The `checked` value of the switch |
| type | string | | The input component property `type`. |
| value | union: string |
number |
bool
| | The value of the component. |