Skip to content

Commit

Permalink
[fixed] Ensure after-open css transitions work in Safari 14 & Mobile …
Browse files Browse the repository at this point in the history
…Safari
  • Loading branch information
Benjamin Padula authored and diasbruno committed May 21, 2021
1 parent 76df16b commit 827796d
Show file tree
Hide file tree
Showing 3 changed files with 88 additions and 56 deletions.
32 changes: 23 additions & 9 deletions specs/Modal.events.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/* eslint-env mocha */
import React from "react";
import ReactDOM from "react-dom";
import "should";
import sinon from "sinon";
import Modal from "react-modal";
Expand All @@ -11,24 +12,37 @@ import {
mouseUpAt,
escKeyDown,
tabKeyDown,
withModal
withModal,
withElementCollector,
createHTMLElement
} from "./helper";

export default () => {
it("should trigger the onAfterOpen callback", () => {
const afterOpenCallback = sinon.spy();
const props = { isOpen: true, onAfterOpen: afterOpenCallback };
withModal(props, null, () => {});
afterOpenCallback.called.should.be.ok();
withElementCollector(() => {
const props = { isOpen: true, onAfterOpen: afterOpenCallback };
const node = createHTMLElement("div");
ReactDOM.render(<Modal {...props} />, node);
requestAnimationFrame(() => {
afterOpenCallback.called.should.be.ok();
ReactDOM.unmountComponentAtNode(node);
});
});
});

it("should call onAfterOpen with overlay and content references", () => {
const afterOpenCallback = sinon.spy();
const props = { isOpen: true, onAfterOpen: afterOpenCallback };
withModal(props, null, modal => {
sinon.assert.calledWith(afterOpenCallback, {
overlayEl: modal.portal.overlay,
contentEl: modal.portal.content
withElementCollector(() => {
const props = { isOpen: true, onAfterOpen: afterOpenCallback };
const node = createHTMLElement("div");
const modal = ReactDOM.render(<Modal {...props} />, node);
requestAnimationFrame(() => {
sinon.assert.calledWith(afterOpenCallback, {
overlayEl: modal.portal.overlay,
contentEl: modal.portal.content
});
ReactDOM.unmountComponentAtNode(node);
});
});
});
Expand Down
94 changes: 55 additions & 39 deletions specs/Modal.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -348,32 +348,42 @@ export default () => {
});

it("overrides content classes with custom object className", () => {
const props = {
isOpen: true,
className: {
base: "myClass",
afterOpen: "myClass_after-open",
beforeClose: "myClass_before-close"
}
};
withModal(props, null, modal => {
mcontent(modal).className.should.be.eql("myClass myClass_after-open");
withElementCollector(() => {
const props = {
isOpen: true,
className: {
base: "myClass",
afterOpen: "myClass_after-open",
beforeClose: "myClass_before-close"
}
};
const node = createHTMLElement("div");
const modal = ReactDOM.render(<Modal {...props} />, node);
requestAnimationFrame(() => {
mcontent(modal).className.should.be.eql("myClass myClass_after-open");
ReactDOM.unmountComponentAtNode(node);
});
});
});

it("overrides overlay classes with custom object overlayClassName", () => {
const props = {
isOpen: true,
overlayClassName: {
base: "myOverlayClass",
afterOpen: "myOverlayClass_after-open",
beforeClose: "myOverlayClass_before-close"
}
};
withModal(props, null, modal => {
moverlay(modal).className.should.be.eql(
"myOverlayClass myOverlayClass_after-open"
);
withElementCollector(() => {
const props = {
isOpen: true,
overlayClassName: {
base: "myOverlayClass",
afterOpen: "myOverlayClass_after-open",
beforeClose: "myOverlayClass_before-close"
}
};
const node = createHTMLElement("div");
const modal = ReactDOM.render(<Modal {...props} />, node);
requestAnimationFrame(() => {
moverlay(modal).className.should.be.eql(
"myOverlayClass myOverlayClass_after-open"
);
ReactDOM.unmountComponentAtNode(node);
});
});
});

Expand Down Expand Up @@ -668,11 +678,18 @@ export default () => {
});

it("adds --after-open for animations", () => {
const props = { isOpen: true };
withModal(props, null, modal => {
withElementCollector(() => {
const rg = /--after-open/i;
rg.test(mcontent(modal).className).should.be.ok();
rg.test(moverlay(modal).className).should.be.ok();
const props = { isOpen: true };
const node = createHTMLElement("div");
const modal = ReactDOM.render(<Modal {...props} />, node);
requestAnimationFrame(() => {
const contentName = modal.portal.content.className;
const overlayName = modal.portal.overlay.className;
rg.test(contentName).should.be.ok();
rg.test(overlayName).should.be.ok();
ReactDOM.unmountComponentAtNode(node);
});
});
});

Expand Down Expand Up @@ -722,25 +739,24 @@ export default () => {
});

it("keeps the modal in the DOM until closeTimeoutMS elapses", done => {
const closeTimeoutMS = 100;
function checkDOM(count) {
const overlay = document.querySelectorAll(".ReactModal__Overlay");
const content = document.querySelectorAll(".ReactModal__Content");
overlay.length.should.be.eql(count);
content.length.should.be.eql(count);
}
withElementCollector(() => {
const closeTimeoutMS = 100;
const props = { isOpen: true, closeTimeoutMS };
const node = createHTMLElement("div");
const modal = ReactDOM.render(<Modal {...props} />, node);

const props = { isOpen: true, closeTimeoutMS };
withModal(props, null, modal => {
modal.portal.closeWithTimeout();

function checkDOM(count) {
const overlay = document.querySelectorAll(".ReactModal__Overlay");
const content = document.querySelectorAll(".ReactModal__Content");
overlay.length.should.be.eql(count);
content.length.should.be.eql(count);
}

// content is still mounted after modal is gone
checkDOM(1);

setTimeout(() => {
// content is unmounted after specified timeout
checkDOM(0);
ReactDOM.unmountComponentAtNode(node);
done();
}, closeTimeoutMS);
});
Expand Down
18 changes: 10 additions & 8 deletions src/components/ModalPortal.js
Original file line number Diff line number Diff line change
Expand Up @@ -222,14 +222,16 @@ export default class ModalPortal extends Component {
}

this.setState({ isOpen: true }, () => {
this.setState({ afterOpen: true });

if (this.props.isOpen && this.props.onAfterOpen) {
this.props.onAfterOpen({
overlayEl: this.overlay,
contentEl: this.content
});
}
requestAnimationFrame(() => {
this.setState({ afterOpen: true });

if (this.props.isOpen && this.props.onAfterOpen) {
this.props.onAfterOpen({
overlayEl: this.overlay,
contentEl: this.content
});
}
});
});
}
};
Expand Down

0 comments on commit 827796d

Please sign in to comment.