Skip to content

Commit 0b1379e

Browse files
chore(Portal): Listen for Escape keypress using addEventListener instead of event-stack
1 parent 74cbda4 commit 0b1379e

File tree

4 files changed

+47
-21
lines changed

4 files changed

+47
-21
lines changed

src/addons/Portal/Portal.js

+2-14
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import EventStack from '@semantic-ui-react/event-stack'
2-
import keyboardKey from 'keyboard-key'
32
import _ from 'lodash'
43
import PropTypes from 'prop-types'
54
import * as React from 'react'
@@ -132,18 +131,6 @@ function Portal(props) {
132131
}
133132
}
134133

135-
const handleEscape = (e) => {
136-
if (!closeOnEscape) {
137-
return
138-
}
139-
if (keyboardKey.getCode(e) !== keyboardKey.Escape) {
140-
return
141-
}
142-
143-
debug('handleEscape()')
144-
closePortal(e)
145-
}
146-
147134
// ----------------------------------------
148135
// Component Event Handlers
149136
// ----------------------------------------
@@ -277,6 +264,8 @@ function Portal(props) {
277264
onMount={() => _.invoke(props, 'onMount', null, props)}
278265
onUnmount={() => _.invoke(props, 'onUnmount', null, props)}
279266
ref={contentRef}
267+
onClose={closePortal}
268+
closeOnEscape={closeOnEscape}
280269
>
281270
{children}
282271
</PortalInner>
@@ -295,7 +284,6 @@ function Portal(props) {
295284
/>
296285
<EventStack name='mousedown' on={handleDocumentMouseDown} pool={eventPool} />
297286
<EventStack name='click' on={handleDocumentClick} pool={eventPool} />
298-
<EventStack name='keydown' on={handleEscape} pool={eventPool} />
299287
</>
300288
)}
301289
{trigger &&

src/addons/Portal/PortalInner.d.ts

+6
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ export interface StrictPortalInnerProps {
2626
* @param {object} data - All props.
2727
*/
2828
onUnmount?: (nothing: null, data: PortalInnerProps) => void
29+
30+
/** Callback called when inner component decides that (respecting the configuration) Portal should close */
31+
onClose: (event: React.MouseEvent<HTMLElement>) => void
32+
33+
/** Controls whether the onClose callback should be invoked when escape is pressed. */
34+
closeOnEscape: boolean
2935
}
3036

3137
declare const PortalInner: React.FC<PortalInnerProps>

src/addons/Portal/PortalInner.js

+30
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
33
import * as React from 'react'
44
import { createPortal } from 'react-dom'
55

6+
import keyboardKey from 'keyboard-key'
67
import { isBrowser, makeDebugger, useEventCallback } from '../../lib'
78
import usePortalElement from './usePortalElement'
89

@@ -12,6 +13,7 @@ const debug = makeDebugger('PortalInner')
1213
* An inner component that allows you to render children outside their parent.
1314
*/
1415
const PortalInner = React.forwardRef(function (props, ref) {
16+
const { closeOnEscape, onClose } = props
1517
const handleMount = useEventCallback(() => _.invoke(props, 'onMount', null, props))
1618
const handleUnmount = useEventCallback(() => _.invoke(props, 'onUnmount', null, props))
1719

@@ -27,6 +29,28 @@ const PortalInner = React.forwardRef(function (props, ref) {
2729
}
2830
}, [])
2931

32+
React.useEffect(() => {
33+
if (!closeOnEscape) {
34+
return
35+
}
36+
37+
/**
38+
* @param {React.KeyboardEvent<HTMLElement>} e
39+
*/
40+
const handleKeyDown = (e) => {
41+
if (keyboardKey.getCode(e) !== keyboardKey.Escape) {
42+
return
43+
}
44+
debug('handleEscape()')
45+
onClose(e)
46+
}
47+
48+
window.addEventListener('keydown', handleKeyDown)
49+
return () => {
50+
window.removeEventListener('keydown', handleKeyDown)
51+
}
52+
}, [closeOnEscape, onClose])
53+
3054
if (!isBrowser()) {
3155
return null
3256
}
@@ -57,6 +81,12 @@ PortalInner.propTypes = {
5781
* @param {object} data - All props.
5882
*/
5983
onUnmount: PropTypes.func,
84+
85+
/** Callback called when inner component decides that (respecting the configuration) Portal should close */
86+
onClose: PropTypes.func.isRequired,
87+
88+
/** Controls whether the portal should close when escape is pressed is displayed. */
89+
closeOnEscape: PropTypes.bool.isRequired,
6090
}
6191

6292
export default PortalInner

test/specs/addons/Portal/PortalInner-test.js

+9-7
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@ import { isBrowser } from 'src/lib'
66
import * as common from 'test/specs/commonTests'
77
import { sandbox } from 'test/utils'
88

9+
const doNothing = () => {}
10+
911
describe('PortalInner', () => {
1012
common.isConformant(PortalInner, {
1113
rendersChildren: false,
12-
requiredProps: { children: <p /> },
14+
requiredProps: { children: <p />, closeOnEscape: false, onClose: doNothing },
1315
forwardsRef: false,
1416
})
1517

@@ -24,7 +26,7 @@ describe('PortalInner', () => {
2426

2527
it('renders `null` when during Server-Side Rendering', () => {
2628
mount(
27-
<PortalInner>
29+
<PortalInner onClose={doNothing} closeOnEscape>
2830
<p />
2931
</PortalInner>,
3032
).should.be.blank()
@@ -37,7 +39,7 @@ describe('PortalInner', () => {
3739
const elementRef = React.createRef()
3840

3941
const wrapper = mount(
40-
<PortalInner ref={portalRef}>
42+
<PortalInner ref={portalRef} onClose={doNothing} closeOnEscape>
4143
<p ref={elementRef} />
4244
</PortalInner>,
4345
)
@@ -57,7 +59,7 @@ describe('PortalInner', () => {
5759
const elementRef = React.createRef()
5860

5961
const wrapper = mount(
60-
<PortalInner ref={portalRef}>
62+
<PortalInner ref={portalRef} onClose={doNothing} closeOnEscape>
6163
<CustomComponent ref={elementRef} />
6264
</PortalInner>,
6365
)
@@ -75,7 +77,7 @@ describe('PortalInner', () => {
7577

7678
const portalRef = React.createRef()
7779
const wrapper = mount(
78-
<PortalInner ref={portalRef}>
80+
<PortalInner ref={portalRef} onClose={doNothing} closeOnEscape>
7981
<CustomComponent />
8082
</PortalInner>,
8183
)
@@ -91,7 +93,7 @@ describe('PortalInner', () => {
9193
it('called when mounting', () => {
9294
const onMount = sandbox.spy()
9395
mount(
94-
<PortalInner onMount={onMount}>
96+
<PortalInner onMount={onMount} onClose={doNothing} closeOnEscape>
9597
<p />
9698
</PortalInner>,
9799
)
@@ -104,7 +106,7 @@ describe('PortalInner', () => {
104106
it('is called only once when unmounting', () => {
105107
const onUnmount = sandbox.spy()
106108
const wrapper = mount(
107-
<PortalInner onUnmount={onUnmount}>
109+
<PortalInner onUnmount={onUnmount} onClose={doNothing} closeOnEscape>
108110
<p />
109111
</PortalInner>,
110112
)

0 commit comments

Comments
 (0)