diff --git a/src/addons/Portal/Portal.js b/src/addons/Portal/Portal.js
index 01cbb2808d..076113b13b 100644
--- a/src/addons/Portal/Portal.js
+++ b/src/addons/Portal/Portal.js
@@ -1,5 +1,4 @@
import EventStack from '@semantic-ui-react/event-stack'
-import keyboardKey from 'keyboard-key'
import _ from 'lodash'
import PropTypes from 'prop-types'
import * as React from 'react'
@@ -132,18 +131,6 @@ function Portal(props) {
}
}
- const handleEscape = (e) => {
- if (!closeOnEscape) {
- return
- }
- if (keyboardKey.getCode(e) !== keyboardKey.Escape) {
- return
- }
-
- debug('handleEscape()')
- closePortal(e)
- }
-
// ----------------------------------------
// Component Event Handlers
// ----------------------------------------
@@ -277,6 +264,8 @@ function Portal(props) {
onMount={() => _.invoke(props, 'onMount', null, props)}
onUnmount={() => _.invoke(props, 'onUnmount', null, props)}
ref={contentRef}
+ onClose={closePortal}
+ closeOnEscape={closeOnEscape}
>
{children}
@@ -295,7 +284,6 @@ function Portal(props) {
/>
-
>
)}
{trigger &&
diff --git a/src/addons/Portal/PortalInner.d.ts b/src/addons/Portal/PortalInner.d.ts
index 518b2ffe6a..017c6e92c3 100644
--- a/src/addons/Portal/PortalInner.d.ts
+++ b/src/addons/Portal/PortalInner.d.ts
@@ -26,6 +26,12 @@ export interface StrictPortalInnerProps {
* @param {object} data - All props.
*/
onUnmount?: (nothing: null, data: PortalInnerProps) => void
+
+ /** Callback called when inner component decides that (respecting the configuration) Portal should close */
+ onClose: (event: React.MouseEvent) => void
+
+ /** Controls whether the onClose callback should be invoked when escape is pressed. */
+ closeOnEscape: boolean
}
declare const PortalInner: React.FC
diff --git a/src/addons/Portal/PortalInner.js b/src/addons/Portal/PortalInner.js
index 854818f379..d004b9a75c 100644
--- a/src/addons/Portal/PortalInner.js
+++ b/src/addons/Portal/PortalInner.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import * as React from 'react'
import { createPortal } from 'react-dom'
+import keyboardKey from 'keyboard-key'
import { isBrowser, makeDebugger, useEventCallback } from '../../lib'
import usePortalElement from './usePortalElement'
@@ -12,6 +13,7 @@ const debug = makeDebugger('PortalInner')
* An inner component that allows you to render children outside their parent.
*/
const PortalInner = React.forwardRef(function (props, ref) {
+ const { closeOnEscape, onClose } = props
const handleMount = useEventCallback(() => _.invoke(props, 'onMount', null, props))
const handleUnmount = useEventCallback(() => _.invoke(props, 'onUnmount', null, props))
@@ -27,6 +29,28 @@ const PortalInner = React.forwardRef(function (props, ref) {
}
}, [])
+ React.useEffect(() => {
+ if (!closeOnEscape) {
+ return
+ }
+
+ /**
+ * @param {React.KeyboardEvent} e
+ */
+ const handleKeyDown = (e) => {
+ if (keyboardKey.getCode(e) !== keyboardKey.Escape) {
+ return
+ }
+ debug('handleEscape()')
+ onClose(e)
+ }
+
+ window.addEventListener('keydown', handleKeyDown)
+ return () => {
+ window.removeEventListener('keydown', handleKeyDown)
+ }
+ }, [closeOnEscape, onClose])
+
if (!isBrowser()) {
return null
}
@@ -57,6 +81,12 @@ PortalInner.propTypes = {
* @param {object} data - All props.
*/
onUnmount: PropTypes.func,
+
+ /** Callback called when inner component decides that (respecting the configuration) Portal should close */
+ onClose: PropTypes.func.isRequired,
+
+ /** Controls whether the portal should close when escape is pressed is displayed. */
+ closeOnEscape: PropTypes.bool.isRequired,
}
export default PortalInner
diff --git a/test/specs/addons/Portal/PortalInner-test.js b/test/specs/addons/Portal/PortalInner-test.js
index 2eadf22b2f..e9ddd588f2 100644
--- a/test/specs/addons/Portal/PortalInner-test.js
+++ b/test/specs/addons/Portal/PortalInner-test.js
@@ -6,10 +6,12 @@ import { isBrowser } from 'src/lib'
import * as common from 'test/specs/commonTests'
import { sandbox } from 'test/utils'
+const doNothing = () => {}
+
describe('PortalInner', () => {
common.isConformant(PortalInner, {
rendersChildren: false,
- requiredProps: { children: },
+ requiredProps: { children: , closeOnEscape: false, onClose: doNothing },
forwardsRef: false,
})
@@ -24,7 +26,7 @@ describe('PortalInner', () => {
it('renders `null` when during Server-Side Rendering', () => {
mount(
-
+
,
).should.be.blank()
@@ -37,7 +39,7 @@ describe('PortalInner', () => {
const elementRef = React.createRef()
const wrapper = mount(
-
+
,
)
@@ -57,7 +59,7 @@ describe('PortalInner', () => {
const elementRef = React.createRef()
const wrapper = mount(
-
+
,
)
@@ -75,7 +77,7 @@ describe('PortalInner', () => {
const portalRef = React.createRef()
const wrapper = mount(
-
+
,
)
@@ -91,7 +93,7 @@ describe('PortalInner', () => {
it('called when mounting', () => {
const onMount = sandbox.spy()
mount(
-
+
,
)
@@ -104,7 +106,7 @@ describe('PortalInner', () => {
it('is called only once when unmounting', () => {
const onUnmount = sandbox.spy()
const wrapper = mount(
-
+
,
)