diff --git a/packages/material-ui/src/ExpansionPanel/ExpansionPanel.js b/packages/material-ui/src/ExpansionPanel/ExpansionPanel.js index e7d37e11e50471..ce6e550c75301d 100644 --- a/packages/material-ui/src/ExpansionPanel/ExpansionPanel.js +++ b/packages/material-ui/src/ExpansionPanel/ExpansionPanel.js @@ -8,6 +8,7 @@ import Collapse from '../Collapse'; import Paper from '../Paper'; import withStyles from '../styles/withStyles'; import { isMuiElement } from '../utils/reactHelpers'; +import { PureProvider } from './ExpansionPanelContext'; export const styles = theme => { const transition = { @@ -112,12 +113,15 @@ function ExpansionPanel(props) { const [expanded, setExpanded] = useMaybeControlled(expandedProp, defaultExpanded); - function handleChange(event) { - setExpanded(isExpanded => !isExpanded); - if (onChange) { - onChange(event, !expanded); - } - } + const handleChange = React.useCallback( + event => { + setExpanded(isExpanded => !isExpanded); + if (onChange) { + onChange(event, !expanded); + } + }, + [expanded, setExpanded, onChange], + ); let summary = null; @@ -135,11 +139,7 @@ function ExpansionPanel(props) { ); if (isMuiElement(child, ['ExpansionPanelSummary'])) { - summary = React.cloneElement(child, { - disabled, - expanded, - onChange: handleChange, - }); + summary = child; return null; } @@ -147,25 +147,27 @@ function ExpansionPanel(props) { }); return ( - - {summary} - - {children} - - + + + {summary} + + {children} + + + ); } @@ -220,6 +222,7 @@ ExpansionPanel.propTypes = { ExpansionPanel.defaultProps = { defaultExpanded: false, disabled: false, + onChange: noop, square: false, TransitionComponent: Collapse, }; diff --git a/packages/material-ui/src/ExpansionPanel/ExpansionPanel.test.js b/packages/material-ui/src/ExpansionPanel/ExpansionPanel.test.js index bd56ac0c491af3..8f1c6af3a0b9c1 100644 --- a/packages/material-ui/src/ExpansionPanel/ExpansionPanel.test.js +++ b/packages/material-ui/src/ExpansionPanel/ExpansionPanel.test.js @@ -2,19 +2,16 @@ import React from 'react'; import PropTypes from 'prop-types'; import { assert } from 'chai'; import { spy } from 'sinon'; -import { createShallow, createMount, getClasses } from '@material-ui/core/test-utils'; -import Collapse from '../Collapse'; +import { createMount, getClasses } from '@material-ui/core/test-utils'; import Paper from '../Paper'; import ExpansionPanel from './ExpansionPanel'; import ExpansionPanelSummary from '../ExpansionPanelSummary'; describe('', () => { let mount; - let shallow; let classes; before(() => { - shallow = createShallow({ dive: true }); mount = createMount(); classes = getClasses(foo); }); @@ -48,23 +45,29 @@ describe('', () => { }); it('should render the custom className and the root class', () => { - const wrapper = shallow(foo); - assert.strictEqual(wrapper.hasClass('test-class-name'), true); - assert.strictEqual(wrapper.hasClass(classes.root), true); + const wrapper = mount(foo); + const root = wrapper.find(`.${classes.root}`).first(); + assert.strictEqual(root.hasClass('test-class-name'), true); }); it('should render the summary and collapse elements', () => { - const wrapper = shallow( - - + const wrapper = mount( + + summary
Hello
, ); - assert.strictEqual(wrapper.childAt(0).type(), ExpansionPanelSummary); - const collapse = wrapper.childAt(1); - assert.strictEqual(collapse.type(), Collapse); - assert.strictEqual(collapse.children().length, 1, 'collapse should have 1 children div'); + assert.strictEqual( + wrapper + .find('[aria-expanded=true]') + .first() + .text(), + 'summary', + ); + + const collapse = wrapper.find('Collapse'); + assert.strictEqual(collapse.text(), 'Hello'); }); it('should handle the expanded prop', () => { @@ -101,21 +104,15 @@ describe('', () => { assert.strictEqual(handleChange.args[0][1], false); }); - it('when undefined onChange and controlled should not call the onChange', () => { - const handleChange = spy(); - const wrapper = mount( - - - , - ); - wrapper.setProps({ onChange: undefined }); - wrapper.find(ExpansionPanelSummary).simulate('click'); - assert.strictEqual(handleChange.callCount, 0); - }); - it('when disabled should have the disabled class', () => { - const wrapper = shallow(foo); - assert.strictEqual(wrapper.hasClass(classes.disabled), true); + const wrapper = mount(foo); + assert.strictEqual( + wrapper + .find(`.${classes.root}`) + .first() + .hasClass(classes.disabled), + true, + ); }); it('should handle the TransitionComponent prop', () => { @@ -149,8 +146,8 @@ describe('', () => { describe('prop: children', () => { it('should accept an empty child', () => { - shallow( - + mount( + {null} , diff --git a/packages/material-ui/src/ExpansionPanel/ExpansionPanelContext.js b/packages/material-ui/src/ExpansionPanel/ExpansionPanelContext.js index b02367c1d9d484..aaf05b7c1c7d98 100644 --- a/packages/material-ui/src/ExpansionPanel/ExpansionPanelContext.js +++ b/packages/material-ui/src/ExpansionPanel/ExpansionPanelContext.js @@ -1,4 +1,5 @@ import React from 'react'; +import PropTypes from 'prop-types'; import warning from 'warning'; const ExpansionPanelContext = React.createContext({ @@ -15,4 +16,14 @@ const ExpansionPanelContext = React.createContext({ }, }); +export const PureProvider = React.memo(props => { + const { children, ...value } = props; + + return {children}; +}); + +PureProvider.propTypes = { + children: PropTypes.node, +}; + export default ExpansionPanelContext; diff --git a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js index f5105c3305a97e..efa9f987e4b4d6 100644 --- a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js +++ b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.js @@ -4,6 +4,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import clsx from 'clsx'; import ButtonBase from '../ButtonBase'; +import { useExpansionPanel } from '../ExpansionPanel'; import withStyles from '../styles/withStyles'; export const styles = theme => { @@ -75,17 +76,15 @@ function ExpansionPanelSummary(props) { children, classes, className, - disabled, - expanded, expandIcon, IconButtonProps, onBlur, - onChange, onClick, onFocusVisible, ...other } = props; const [focused, setFocused] = React.useState(false); + const { disabled, expanded, onChange } = useExpansionPanel(); function handleFocusVisible(event) { setFocused(true); @@ -191,7 +190,6 @@ const noop = () => {}; ExpansionPanelSummary.defaultProps = { disabled: false, onBlur: noop, - onChange: noop, onClick: noop, onFocusVisible: noop, }; diff --git a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js index b33f8e057c1270..1314d9d5941976 100644 --- a/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js +++ b/packages/material-ui/src/ExpansionPanelSummary/ExpansionPanelSummary.test.js @@ -8,9 +8,10 @@ import { getClasses, } from '@material-ui/core/test-utils'; import ButtonBase from '../ButtonBase'; +import ExpansionPanel from '../ExpansionPanel'; import ExpansionPanelSummary from './ExpansionPanelSummary'; -describe('', () => { +describe.only('', () => { let mount; let shallow; let classes; @@ -105,11 +106,17 @@ describe('', () => { return result; }, {}); - const wrapper = shallow(); + const wrapper = mount( + + + , + ); + + const summary = wrapper.find('[aria-expanded]').first(); events.forEach(n => { const event = n.charAt(2).toLowerCase() + n.slice(3); - wrapper.simulate(event, { persist: () => {} }); + summary.simulate(event, { persist: () => {} }); assert.strictEqual(handlers[n].callCount, 1, `should have called the ${n} handler`); }); }); @@ -118,7 +125,11 @@ describe('', () => { describe('prop: onChange', () => { it('fires onChange if the summary control is clicked', () => { const handleChange = spy(); - const wrapper = mount(); + const wrapper = mount( + + + , + ); const control = findOutermostIntrinsic(wrapper.find('[aria-expanded]')); const eventMock = 'woofExpansionPanelSummary'; @@ -132,8 +143,15 @@ describe('', () => { describe('prop: click', () => { it('should trigger onClick', () => { const handleClick = spy(); - const wrapper = shallow(); - wrapper.simulate('click'); + const wrapper = mount( + {}}> + + , + ); + wrapper + .find('[aria-expanded]') + .first() + .simulate('click'); assert.strictEqual(handleClick.callCount, 1); }); });