Skip to content

Commit 3f851ce

Browse files
ZeeshanTambolioliviertassinariDiegoAndai
authored
[material-ui] Prevent ownerState propagation for transition slots (#44401)
Signed-off-by: Zeeshan Tamboli <[email protected]> Signed-off-by: Diego Andai <[email protected]> Co-authored-by: Olivier Tassinari <[email protected]> Co-authored-by: Diego Andai <[email protected]>
1 parent 523b329 commit 3f851ce

File tree

8 files changed

+84
-20
lines changed

8 files changed

+84
-20
lines changed

packages/mui-material/src/Accordion/Accordion.test.js

+50-1
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,14 @@ import * as React from 'react';
22
import PropTypes from 'prop-types';
33
import { expect } from 'chai';
44
import { spy } from 'sinon';
5-
import { createRenderer, fireEvent, reactMajor } from '@mui/internal-test-utils';
5+
import { createRenderer, fireEvent, reactMajor, screen } from '@mui/internal-test-utils';
66
import Accordion, { accordionClasses as classes } from '@mui/material/Accordion';
77
import Paper from '@mui/material/Paper';
8+
import Collapse from '@mui/material/Collapse';
9+
import Fade from '@mui/material/Fade';
10+
import Slide from '@mui/material/Slide';
11+
import Grow from '@mui/material/Grow';
12+
import Zoom from '@mui/material/Zoom';
813
import AccordionSummary from '@mui/material/AccordionSummary';
914
import describeConformance from '../../test/describeConformance';
1015

@@ -261,4 +266,48 @@ describe('<Accordion />', () => {
261266
expect(queryByTestId('details')).to.equal(null);
262267
});
263268
});
269+
270+
describe('should not forward ownerState prop to the underlying DOM element when using transition slot', () => {
271+
const transitions = [
272+
{
273+
component: Collapse,
274+
name: 'Collapse',
275+
},
276+
{
277+
component: Fade,
278+
name: 'Fade',
279+
},
280+
{
281+
component: Grow,
282+
name: 'Grow',
283+
},
284+
{
285+
component: Slide,
286+
name: 'Slide',
287+
},
288+
{
289+
component: Zoom,
290+
name: 'Zoom',
291+
},
292+
];
293+
294+
transitions.forEach((transition) => {
295+
it(transition.name, () => {
296+
render(
297+
<Accordion
298+
defaultExpanded
299+
slots={{
300+
transition: transition.component,
301+
}}
302+
slotProps={{ transition: { timeout: 400 } }}
303+
>
304+
<AccordionSummary>Summary</AccordionSummary>
305+
Details
306+
</Accordion>,
307+
);
308+
309+
expect(screen.getByRole('region')).not.to.have.attribute('ownerstate');
310+
});
311+
});
312+
});
264313
});

packages/mui-material/src/Backdrop/Backdrop.js

+1-7
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@ import useSlot from '../utils/useSlot';
99
import Fade from '../Fade';
1010
import { getBackdropUtilityClass } from './backdropClasses';
1111

12-
const removeOwnerState = (props) => {
13-
const { ownerState, ...rest } = props;
14-
return rest;
15-
};
16-
1712
const useUtilityClasses = (ownerState) => {
1813
const { classes, invisible } = ownerState;
1914

@@ -101,10 +96,9 @@ const Backdrop = React.forwardRef(function Backdrop(inProps, ref) {
10196
externalForwardedProps,
10297
ownerState,
10398
});
104-
const transitionPropsRemoved = removeOwnerState(transitionProps);
10599

106100
return (
107-
<TransitionSlot in={open} timeout={transitionDuration} {...other} {...transitionPropsRemoved}>
101+
<TransitionSlot in={open} timeout={transitionDuration} {...other} {...transitionProps}>
108102
<RootSlot aria-hidden {...rootProps} classes={classes} ref={ref}>
109103
{children}
110104
</RootSlot>

packages/mui-material/src/Collapse/Collapse.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -308,7 +308,8 @@ const Collapse = React.forwardRef(function Collapse(inProps, ref) {
308308
timeout={timeout === 'auto' ? null : timeout}
309309
{...other}
310310
>
311-
{(state, childProps) => (
311+
{/* Destructure child props to prevent the component's "ownerState" from being overridden by incomingOwnerState. */}
312+
{(state, { ownerState: incomingOwnerState, ...restChildProps }) => (
312313
<CollapseRoot
313314
as={component}
314315
className={clsx(
@@ -324,10 +325,8 @@ const Collapse = React.forwardRef(function Collapse(inProps, ref) {
324325
...style,
325326
}}
326327
ref={handleRef}
327-
{...childProps}
328-
// `ownerState` is set after `childProps` to override any existing `ownerState` property in `childProps`
329-
// that might have been forwarded from the Transition component.
330328
ownerState={{ ...ownerState, state }}
329+
{...restChildProps}
331330
>
332331
<CollapseWrapper
333332
ownerState={{ ...ownerState, state }}

packages/mui-material/src/Collapse/Collapse.test.js

+18
Original file line numberDiff line numberDiff line change
@@ -268,4 +268,22 @@ describe('<Collapse />', () => {
268268
expect(handleExiting.args[0][0].style.height).to.equal(collapsedSize);
269269
});
270270
});
271+
272+
// Test for https://github.com/mui/material-ui/issues/40653
273+
it('should render correctly when external ownerState prop is passed', function test() {
274+
if (/jsdom/.test(window.navigator.userAgent)) {
275+
this.skip();
276+
}
277+
278+
const { container } = render(
279+
<Collapse in ownerState={{}}>
280+
<div style={{ height: '100px' }} />
281+
</Collapse>,
282+
);
283+
const collapse = container.firstChild;
284+
285+
expect(collapse).toHaveComputedStyle({
286+
height: '100px',
287+
});
288+
});
271289
});

packages/mui-material/src/Fade/Fade.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ const Fade = React.forwardRef(function Fade(props, ref) {
128128
timeout={timeout}
129129
{...other}
130130
>
131-
{(state, childProps) => {
131+
{/* Ensure "ownerState" is not forwarded to the child DOM element when a direct HTML element is used. This avoids unexpected behavior since "ownerState" is intended for internal styling, component props and not as a DOM attribute. */}
132+
{(state, { ownerState, ...restChildProps }) => {
132133
return React.cloneElement(children, {
133134
style: {
134135
opacity: 0,
@@ -138,7 +139,7 @@ const Fade = React.forwardRef(function Fade(props, ref) {
138139
...children.props.style,
139140
},
140141
ref: handleRef,
141-
...childProps,
142+
...restChildProps,
142143
});
143144
}}
144145
</TransitionComponent>

packages/mui-material/src/Grow/Grow.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ const Grow = React.forwardRef(function Grow(props, ref) {
189189
timeout={timeout === 'auto' ? null : timeout}
190190
{...other}
191191
>
192-
{(state, childProps) => {
192+
{/* Ensure "ownerState" is not forwarded to the child DOM element when a direct HTML element is used. This avoids unexpected behavior since "ownerState" is intended for internal styling, component props and not as a DOM attribute. */}
193+
{(state, { ownerState, ...restChildProps }) => {
193194
return React.cloneElement(children, {
194195
style: {
195196
opacity: 0,
@@ -200,7 +201,7 @@ const Grow = React.forwardRef(function Grow(props, ref) {
200201
...children.props.style,
201202
},
202203
ref: handleRef,
203-
...childProps,
204+
...restChildProps,
204205
});
205206
}}
206207
</TransitionComponent>

packages/mui-material/src/Slide/Slide.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -252,15 +252,16 @@ const Slide = React.forwardRef(function Slide(props, ref) {
252252
timeout={timeout}
253253
{...other}
254254
>
255-
{(state, childProps) => {
255+
{/* Ensure "ownerState" is not forwarded to the child DOM element when a direct HTML element is used. This avoids unexpected behavior since "ownerState" is intended for internal styling, component props and not as a DOM attribute. */}
256+
{(state, { ownerState, ...restChildProps }) => {
256257
return React.cloneElement(children, {
257258
ref: handleRef,
258259
style: {
259260
visibility: state === 'exited' && !inProp ? 'hidden' : undefined,
260261
...style,
261262
...children.props.style,
262263
},
263-
...childProps,
264+
...restChildProps,
264265
});
265266
}}
266267
</TransitionComponent>

packages/mui-material/src/Zoom/Zoom.js

+3-2
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ const Zoom = React.forwardRef(function Zoom(props, ref) {
128128
timeout={timeout}
129129
{...other}
130130
>
131-
{(state, childProps) => {
131+
{/* Ensure "ownerState" is not forwarded to the child DOM element when a direct HTML element is used. This avoids unexpected behavior since "ownerState" is intended for internal styling, component props and not as a DOM attribute. */}
132+
{(state, { ownerState, ...restChildProps }) => {
132133
return React.cloneElement(children, {
133134
style: {
134135
transform: 'scale(0)',
@@ -138,7 +139,7 @@ const Zoom = React.forwardRef(function Zoom(props, ref) {
138139
...children.props.style,
139140
},
140141
ref: handleRef,
141-
...childProps,
142+
...restChildProps,
142143
});
143144
}}
144145
</TransitionComponent>

0 commit comments

Comments
 (0)