Home > Software engineering >  React test library does not work with Modal Portal
React test library does not work with Modal Portal

Time:10-11

I have written a component for Modal Window, put it in createPortal. I wanted to write a test for that, but a got an error: console.error Error: Uncaught [Error: Target container is not a DOM element.] For some reasons test did not see my component. I do not have any idea why it work this way. If somebody knows, please write, thank you in advance! My test looks so:

test('modal', () => {
  const handleClose = jest.fn();

  const {getByText} = render(<Modal onClose={handleClose} isOpen title={'title'} />);
  expect(getByText('title')).toBeTruthy();
});

My component looks so:

interface IModalProps {
  isOpen: boolean;
  title?: string;
  className?: string;
  onClose: () => void;
  content?: React.ReactChildren | React.ReactChild
}

const Modal = ({isOpen, title, content, onClose, className}: IModalProps) => {

  const keydownHandler = ({keyCode}:KeyboardEvent) => {
    if (keyCode === 27) {
      onClose();
    }
  };

  React.useEffect(() => {
    document.addEventListener('keydown', keydownHandler);
    return () => document.removeEventListener('keydown', keydownHandler);
  });

  const modalInPortal = () => (
    <div
      className={classNames(
        'modal-overlay',
        isOpen && 'active')}
      onClick={onClose}
    >
      <div
        className={classNames(
          className,
          'modal' )}
        onClick={(e) => e.stopPropagation()}
      >
        <div className="modal-block-content">
          <span
            className="close-btn"
            onClick={onClose}
          >
        &times;
          </span>
          <h2 className="modal-title">{title}</h2>
          {content && <div className="modal-body">{content}</div>}
          <div className="modal-footer">
            <Button
              color={'light'}
              style={{color:'blue'}}
              size="lg"
              onClick={onClose}
            >
              Cancel
            </Button>
            <Button size="lg">Save</Button>
          </div>
        </div>
      </div>
    </div>
  );

  return createPortal( modalInPortal(), document.getElementById('portal'));
};

CodePudding user response:

Most likely, in production, you have a HTML element in your index.html that has an id of portal.

Jest doesn't have that same HTML, so when it tries to portal, it can't find it and blows up.

Usually to fix this, you need to add a setupFilesAfterEnv to your jest config that points to a new file that builds up that HTML outside of reacts remit.

Inside this file, youd have:

const portalEl = document.createElement('div')
portalEl.setAttribute('id', 'portal')
document.body.appendChild(portalEl)

Now the test environment is like your real one for all tests (this file is globally applied).

When asserting on the contents you will also probably need to use screen since the actual modal is loaded outside the container.

  • Related