From ffdde572de1e92b8d0e34c651b38fcf8a7c06577 Mon Sep 17 00:00:00 2001 From: Minh Nguyen Date: Thu, 19 Dec 2019 10:03:44 +0000 Subject: [PATCH 1/2] [RadioGroup] Add useRadioGroup Hook Closes #18918 --- packages/material-ui/src/Radio/Radio.js | 4 +- .../src/RadioGroup/RadioGroup.test.js | 60 ++++++++++++++++++- .../material-ui/src/RadioGroup/index.d.ts | 1 + packages/material-ui/src/RadioGroup/index.js | 1 + .../src/RadioGroup/useRadioGroup.d.ts | 11 ++++ .../src/RadioGroup/useRadioGroup.js | 6 ++ 6 files changed, 80 insertions(+), 3 deletions(-) create mode 100644 packages/material-ui/src/RadioGroup/useRadioGroup.d.ts create mode 100644 packages/material-ui/src/RadioGroup/useRadioGroup.js diff --git a/packages/material-ui/src/Radio/Radio.js b/packages/material-ui/src/Radio/Radio.js index 800d33dbde5a55..9fee569953d487 100644 --- a/packages/material-ui/src/Radio/Radio.js +++ b/packages/material-ui/src/Radio/Radio.js @@ -8,7 +8,7 @@ import { fade } from '../styles/colorManipulator'; import capitalize from '../utils/capitalize'; import createChainedFunction from '../utils/createChainedFunction'; import withStyles from '../styles/withStyles'; -import RadioGroupContext from '../RadioGroup/RadioGroupContext'; +import useRadioGroup from '../RadioGroup/useRadioGroup'; export const styles = theme => ({ /* Styles applied to the root element. */ @@ -67,7 +67,7 @@ const Radio = React.forwardRef(function Radio(props, ref) { size = 'medium', ...other } = props; - const radioGroup = React.useContext(RadioGroupContext); + const radioGroup = useRadioGroup(); let checked = checkedProp; const onChange = createChainedFunction(onChangeProp, radioGroup && radioGroup.onChange); diff --git a/packages/material-ui/src/RadioGroup/RadioGroup.test.js b/packages/material-ui/src/RadioGroup/RadioGroup.test.js index e426f44575253a..76a73671ffd3d6 100644 --- a/packages/material-ui/src/RadioGroup/RadioGroup.test.js +++ b/packages/material-ui/src/RadioGroup/RadioGroup.test.js @@ -1,16 +1,19 @@ import React from 'react'; -import { assert } from 'chai'; +import { assert, expect } from 'chai'; import { spy } from 'sinon'; import * as PropTypes from 'prop-types'; import { createMount, findOutermostIntrinsic } from '@material-ui/core/test-utils'; import describeConformance from '../test-utils/describeConformance'; +import { createClientRender } from 'test/utils/createClientRender'; import FormGroup from '../FormGroup'; import Radio from '../Radio'; import RadioGroup from './RadioGroup'; import consoleErrorMock from 'test/utils/consoleErrorMock'; +import useRadioGroup from './useRadioGroup'; describe('', () => { let mount; + const render = createClientRender({ strict: true }); before(() => { // StrictModeViolation: test uses #simulate @@ -257,6 +260,61 @@ describe('', () => { }); }); + describe('useRadioGroup', () => { + const RadioGroupController = React.forwardRef((_, ref) => { + const radioGroup = useRadioGroup(); + React.useImperativeHandle(ref, () => radioGroup, [radioGroup]); + return null; + }); + + const RadioGroupControlled = React.forwardRef(function RadioGroupControlled(props, ref) { + return ( + + + + ); + }); + + describe('from props', () => { + it('should have the name prop from the instance', () => { + const radioGroupRef = React.createRef(); + const { setProps } = render(); + + expect(radioGroupRef.current).to.have.property('name', 'group'); + + setProps({ name: 'anotherGroup' }); + expect(radioGroupRef.current).to.have.property('name', 'anotherGroup'); + }); + + it('should have the value prop from the instance', () => { + const radioGroupRef = React.createRef(); + const { setProps } = render(); + + expect(radioGroupRef.current).to.have.property('value', ''); + + setProps({ value: 'one' }); + expect(radioGroupRef.current).to.have.property('value', 'one'); + }); + }); + + describe('callbacks', () => { + describe('onChange', () => { + it('should set the value state', () => { + const radioGroupRef = React.createRef(); + render(); + + expect(radioGroupRef.current).to.have.property('value', 'zero'); + + radioGroupRef.current.onChange({ target: { value: 'one' } }); + expect(radioGroupRef.current).to.have.property('value', 'one'); + + radioGroupRef.current.onChange({ target: { value: 'two' } }); + expect(radioGroupRef.current).to.have.property('value', 'two'); + }); + }); + }); + }); + describe('warnings', () => { beforeEach(() => { consoleErrorMock.spy(); diff --git a/packages/material-ui/src/RadioGroup/index.d.ts b/packages/material-ui/src/RadioGroup/index.d.ts index e7e280f415004c..b79185835ed329 100644 --- a/packages/material-ui/src/RadioGroup/index.d.ts +++ b/packages/material-ui/src/RadioGroup/index.d.ts @@ -1,2 +1,3 @@ export { default } from './RadioGroup'; export * from './RadioGroup'; +export { default as useRadioGroup, RadioGroupState } from './useRadioGroup'; diff --git a/packages/material-ui/src/RadioGroup/index.js b/packages/material-ui/src/RadioGroup/index.js index 43f73d549d23b4..20284cadcf592c 100644 --- a/packages/material-ui/src/RadioGroup/index.js +++ b/packages/material-ui/src/RadioGroup/index.js @@ -1 +1,2 @@ export { default } from './RadioGroup'; +export { default as useRadioGroup } from './useRadioGroup'; diff --git a/packages/material-ui/src/RadioGroup/useRadioGroup.d.ts b/packages/material-ui/src/RadioGroup/useRadioGroup.d.ts new file mode 100644 index 00000000000000..78e02dd94c9ea2 --- /dev/null +++ b/packages/material-ui/src/RadioGroup/useRadioGroup.d.ts @@ -0,0 +1,11 @@ +import { Context } from 'react'; +import { RadioGroupProps } from './RadioGroup'; + +// shut off automatic exporting +export {}; + +type ContextFromPropsKey = 'name' | 'onChange' | 'value'; + +export interface RadioGroupState extends Pick {} + +export default function useRadioGroup(): RadioGroupState; diff --git a/packages/material-ui/src/RadioGroup/useRadioGroup.js b/packages/material-ui/src/RadioGroup/useRadioGroup.js new file mode 100644 index 00000000000000..47b2186f39fe42 --- /dev/null +++ b/packages/material-ui/src/RadioGroup/useRadioGroup.js @@ -0,0 +1,6 @@ +import React from 'react'; +import RadioGroupContext from './RadioGroupContext'; + +export default function useRadioGroup() { + return React.useContext(RadioGroupContext); +} From 83a205eeda2f6ba325b1d00200557bdc779c8968 Mon Sep 17 00:00:00 2001 From: Minh Nguyen Date: Fri, 27 Dec 2019 14:34:34 +0000 Subject: [PATCH 2/2] Inline `ContextFromPropsKey` --- packages/material-ui/src/RadioGroup/useRadioGroup.d.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/material-ui/src/RadioGroup/useRadioGroup.d.ts b/packages/material-ui/src/RadioGroup/useRadioGroup.d.ts index 78e02dd94c9ea2..8277f3aca3b7b6 100644 --- a/packages/material-ui/src/RadioGroup/useRadioGroup.d.ts +++ b/packages/material-ui/src/RadioGroup/useRadioGroup.d.ts @@ -4,8 +4,6 @@ import { RadioGroupProps } from './RadioGroup'; // shut off automatic exporting export {}; -type ContextFromPropsKey = 'name' | 'onChange' | 'value'; - -export interface RadioGroupState extends Pick {} +export interface RadioGroupState extends Pick {} export default function useRadioGroup(): RadioGroupState;