Skip to content

Commit

Permalink
chore(Portal): Listen for Escape keypress using addEventListener inst…
Browse files Browse the repository at this point in the history
…ead of event-stack
  • Loading branch information
dominikdosoudil committed Nov 21, 2024
1 parent 74cbda4 commit 0b1379e
Show file tree
Hide file tree
Showing 4 changed files with 47 additions and 21 deletions.
16 changes: 2 additions & 14 deletions src/addons/Portal/Portal.js
Original file line number Diff line number Diff line change
@@ -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'
Expand Down Expand Up @@ -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
// ----------------------------------------
Expand Down Expand Up @@ -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}
</PortalInner>
Expand All @@ -295,7 +284,6 @@ function Portal(props) {
/>
<EventStack name='mousedown' on={handleDocumentMouseDown} pool={eventPool} />
<EventStack name='click' on={handleDocumentClick} pool={eventPool} />
<EventStack name='keydown' on={handleEscape} pool={eventPool} />
</>
)}
{trigger &&
Expand Down
6 changes: 6 additions & 0 deletions src/addons/Portal/PortalInner.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<HTMLElement>) => void

/** Controls whether the onClose callback should be invoked when escape is pressed. */
closeOnEscape: boolean
}

declare const PortalInner: React.FC<PortalInnerProps>
Expand Down
30 changes: 30 additions & 0 deletions src/addons/Portal/PortalInner.js
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand All @@ -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))

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

React.useEffect(() => {
if (!closeOnEscape) {
return
}

/**
* @param {React.KeyboardEvent<HTMLElement>} 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
}
Expand Down Expand Up @@ -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
16 changes: 9 additions & 7 deletions test/specs/addons/Portal/PortalInner-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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: <p /> },
requiredProps: { children: <p />, closeOnEscape: false, onClose: doNothing },
forwardsRef: false,
})

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

it('renders `null` when during Server-Side Rendering', () => {
mount(
<PortalInner>
<PortalInner onClose={doNothing} closeOnEscape>
<p />
</PortalInner>,
).should.be.blank()
Expand All @@ -37,7 +39,7 @@ describe('PortalInner', () => {
const elementRef = React.createRef()

const wrapper = mount(
<PortalInner ref={portalRef}>
<PortalInner ref={portalRef} onClose={doNothing} closeOnEscape>
<p ref={elementRef} />
</PortalInner>,
)
Expand All @@ -57,7 +59,7 @@ describe('PortalInner', () => {
const elementRef = React.createRef()

const wrapper = mount(
<PortalInner ref={portalRef}>
<PortalInner ref={portalRef} onClose={doNothing} closeOnEscape>
<CustomComponent ref={elementRef} />
</PortalInner>,
)
Expand All @@ -75,7 +77,7 @@ describe('PortalInner', () => {

const portalRef = React.createRef()
const wrapper = mount(
<PortalInner ref={portalRef}>
<PortalInner ref={portalRef} onClose={doNothing} closeOnEscape>
<CustomComponent />
</PortalInner>,
)
Expand All @@ -91,7 +93,7 @@ describe('PortalInner', () => {
it('called when mounting', () => {
const onMount = sandbox.spy()
mount(
<PortalInner onMount={onMount}>
<PortalInner onMount={onMount} onClose={doNothing} closeOnEscape>
<p />
</PortalInner>,
)
Expand All @@ -104,7 +106,7 @@ describe('PortalInner', () => {
it('is called only once when unmounting', () => {
const onUnmount = sandbox.spy()
const wrapper = mount(
<PortalInner onUnmount={onUnmount}>
<PortalInner onUnmount={onUnmount} onClose={doNothing} closeOnEscape>
<p />
</PortalInner>,
)
Expand Down

0 comments on commit 0b1379e

Please sign in to comment.