Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions packages/mui-material/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,9 @@
"engines": {
"node": ">=14.0.0"
},
"browser": {
"react-transition-group/cjs/TransitionGroupContext.js": "react-transition-group/esm/TransitionGroupContext.js"
},
"exports": {
".": "./src/index.js",
"./ButtonBase/TouchRipple": "./src/ButtonBase/TouchRipple.js",
Expand Down
48 changes: 48 additions & 0 deletions packages/mui-material/src/internal/Transition.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import { TransitionGroup } from 'react-transition-group';
import TransitionGroupContext from 'react-transition-group/TransitionGroupContext';
import { act, createRenderer, screen } from '@mui/internal-test-utils';
import Transition from './Transition';
Expand Down Expand Up @@ -990,6 +991,53 @@ describe('<Transition />', () => {
});
});

describe('react-transition-group public TransitionGroup interop', () => {
it('child added to an already-mounted TransitionGroup enters with isAppearing=false', async () => {
const handlers = { onEnter: spy(), onEntered: spy() };
let done: (() => void) | null = null;
const addEndListener = (_node: HTMLElement, next: () => void) => {
done = next;
};

function ChildWrapper() {
const [shouldRender, setShouldRender] = React.useState(false);
return (
<React.Fragment>
<button type="button" onClick={() => setShouldRender(true)}>
add
</button>
<TransitionGroup component={null}>
{shouldRender ? (
<TestHarness
key="item"
appear={false}
timeout={null}
addEndListener={addEndListener}
handlers={handlers}
/>
) : null}
</TransitionGroup>
</React.Fragment>
);
}

const { user } = render(<ChildWrapper />);
await user.click(screen.getByRole('button', { name: 'add' }));

expect(screen.getByTestId('target')).to.have.attribute('data-status', 'entering');
expect(handlers.onEnter.callCount).to.equal(1);
expect(handlers.onEnter.args[0][0]).to.equal(false);

act(() => {
done!();
});

expect(screen.getByTestId('target')).to.have.attribute('data-status', 'entered');
expect(handlers.onEntered.callCount).to.equal(1);
expect(handlers.onEntered.args[0][0]).to.equal(false);
});
});

describe('react-transition-group TransitionGroupContext user interactions', () => {
const mountedGroup = { isMounting: false };

Expand Down
5 changes: 4 additions & 1 deletion packages/mui-material/src/internal/Transition.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import useEnhancedEffect from '@mui/utils/useEnhancedEffect';
import useValueAsRef from '@mui/utils/useValueAsRef';
// Material UI transitions must still work inside react-transition-group's TransitionGroup.
// Import only its context module; do not import its Transition or TransitionGroup components.
import TransitionGroupContext from 'react-transition-group/TransitionGroupContext';
// Use RTG's explicit CJS file for Node ESM/SSR; package.json's `browser` field redirects
// browser bundles to RTG's ESM file.
// eslint-disable-next-line import/extensions -- Node ESM needs the explicit .js extension.
import TransitionGroupContext from 'react-transition-group/cjs/TransitionGroupContext.js';
import { reflow } from '../transitions/utils';

type RenderedTransitionStatus = 'entering' | 'entered' | 'exiting' | 'exited';
Expand Down
11 changes: 11 additions & 0 deletions packages/mui-material/src/internal/react-transition-group.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,14 @@ declare module 'react-transition-group/TransitionGroupContext' {
const TransitionGroupContext: React.Context<TransitionGroupContextValue | null>;
export default TransitionGroupContext;
}

declare module 'react-transition-group/cjs/TransitionGroupContext.js' {
import * as React from 'react';

interface TransitionGroupContextValue {
isMounting: boolean;
}

const TransitionGroupContext: React.Context<TransitionGroupContextValue | null>;
export default TransitionGroupContext;
}
Loading