Files
react-test/packages/mui-material/src/Portal/Portal.test.tsx
how2ice 005cf56baf
Some checks failed
No response / noResponse (push) Has been cancelled
CI / Continuous releases (push) Has been cancelled
CI / test-dev (macos-latest) (push) Has been cancelled
CI / test-dev (ubuntu-latest) (push) Has been cancelled
CI / test-dev (windows-latest) (push) Has been cancelled
Maintenance / main (push) Has been cancelled
Scorecards supply-chain security / Scorecards analysis (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled
init project
2025-12-12 14:26:25 +09:00

216 lines
6.3 KiB
TypeScript

import * as React from 'react';
import { expect } from 'chai';
import { spy } from 'sinon';
import { createRenderer, reactMajor, isJsdom } from '@mui/internal-test-utils';
import Portal, { PortalProps } from '@mui/material/Portal';
describe('<Portal />', () => {
const { render, renderToString } = createRenderer();
describe.skipIf(!isJsdom())('server-side', () => {
it('render nothing on the server', () => {
const { container } = renderToString(
<Portal>
<div>Bar</div>
</Portal>,
);
expect(container.firstChild).to.equal(null);
});
});
describe('ref', () => {
it('should have access to the mountNode when disabledPortal={false}', () => {
const refSpy = spy();
const { unmount } = render(
<Portal ref={refSpy}>
<h1>Foo</h1>
</Portal>,
);
expect(refSpy.args).to.deep.equal([[document.body]]);
unmount();
expect(refSpy.args).to.deep.equal([[document.body], [null]]);
});
it('should have access to the mountNode when disabledPortal={true}', () => {
const refSpy = spy();
const { unmount } = render(
<Portal disablePortal ref={refSpy}>
<h1 className="woofPortal">Foo</h1>
</Portal>,
{ strict: reactMajor <= 18 },
);
const mountNode = document.querySelector('.woofPortal');
expect(refSpy.args).to.deep.equal([[mountNode]]);
unmount();
expect(refSpy.args).to.deep.equal([[mountNode], [null]]);
});
it('should have access to the mountNode when switching disabledPortal', () => {
const refSpy = spy();
const { setProps, unmount } = render(
<Portal disablePortal ref={refSpy}>
<h1 className="woofPortal">Foo</h1>
</Portal>,
{ strict: reactMajor <= 18 },
);
const mountNode = document.querySelector('.woofPortal');
expect(refSpy.args).to.deep.equal([[mountNode]]);
setProps({
disablePortal: false,
ref: refSpy,
});
expect(refSpy.args).to.deep.equal([[mountNode], [null], [document.body]]);
unmount();
expect(refSpy.args).to.deep.equal([[mountNode], [null], [document.body], [null]]);
});
});
it('should render in a different node', () => {
render(
<div id="test1">
<h1 className="woofPortal1">Foo</h1>
<Portal>
<h1 className="woofPortal2">Foo</h1>
</Portal>
</div>,
);
const rootElement = document.querySelector<HTMLDivElement>('#test1')!;
expect(rootElement.contains(document.querySelector('.woofPortal1'))).to.equal(true);
expect(rootElement.contains(document.querySelector('.woofPortal2'))).to.equal(false);
});
it('should unmount when parent unmounts', () => {
function Child() {
const containerRef = React.useRef<HTMLDivElement>(null);
return (
<div>
<div ref={containerRef} />
<Portal container={() => containerRef.current}>
<div id="test1" />
</Portal>
</div>
);
}
function Parent(props: { show?: boolean }) {
const { show = true } = props;
return <div>{show ? <Child /> : null}</div>;
}
const { setProps } = render(<Parent />);
expect(document.querySelectorAll('#test1').length).to.equal(1);
setProps({ show: false });
expect(document.querySelectorAll('#test1').length).to.equal(0);
});
it('should render overlay into container (document)', () => {
render(
<Portal>
<div className="test2" />
<div className="test2" />
</Portal>,
);
expect(document.querySelectorAll('.test2').length).to.equal(2);
});
it('should render overlay into container (DOMNode)', () => {
const container = document.createElement('div');
render(
<Portal container={container}>
<div id="test2" />
</Portal>,
);
expect(container.querySelectorAll('#test2').length).to.equal(1);
});
it('should change container on prop change', () => {
type ContainerProps = {
disablePortal?: boolean;
containerElement?: boolean;
};
function ContainerTest(props: ContainerProps) {
const { containerElement = false, disablePortal = true } = props;
const containerRef = React.useRef<HTMLElement>(null);
const container = React.useCallback(
() => (containerElement ? containerRef.current : null),
[containerElement],
);
return (
<span>
<strong ref={containerRef} />
<Portal disablePortal={disablePortal} container={container}>
<div id="test3" />
</Portal>
</span>
);
}
const { setProps } = render(<ContainerTest />);
expect(document.querySelector('#test3')?.parentElement?.nodeName).to.equal('SPAN');
setProps({
containerElement: true,
disablePortal: true,
});
expect(document.querySelector('#test3')?.parentElement?.nodeName).to.equal('SPAN');
setProps({
containerElement: true,
disablePortal: false,
});
expect(document.querySelector('#test3')?.parentElement?.nodeName).to.equal('STRONG');
setProps({
containerElement: false,
disablePortal: false,
});
expect(document.querySelector('#test3')?.parentElement?.nodeName).to.equal('BODY');
});
it('should call ref after child effect', () => {
const callOrder: Array<string> = [];
const handleRef = (node: Element | null) => {
if (node) {
callOrder.push('ref');
}
};
const updateFunction = () => {
callOrder.push('effect');
};
function Test(props: PortalProps) {
const { container } = props;
const containerRef = React.useRef<PortalProps['container']>(null);
React.useEffect(() => {
if (containerRef.current !== container) {
updateFunction();
}
containerRef.current = container;
}, [container]);
return (
<Portal ref={handleRef} container={container}>
<div />
</Portal>
);
}
const { setProps } = render(<Test container={document.createElement('div')} />);
setProps({ container: null });
setProps({ container: document.createElement('div') });
setProps({ container: null });
expect(callOrder).to.deep.equal([
'effect',
'ref',
'effect',
'ref',
'effect',
'ref',
'effect',
'ref',
]);
});
});