Skip to content

Commit 3f40522

Browse files
authored
Revert "Overlay: Attach escape handler to overlay container (#1824)" (#1856)
* Revert "Overlay: Attach escape handler to overlay container (#1824)" This reverts commit 4eab65e. * add changeset
1 parent 96473f3 commit 3f40522

File tree

5 files changed

+13
-171
lines changed

5 files changed

+13
-171
lines changed

.changeset/revert-1824.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@primer/react': patch
3+
---
4+
5+
Revert "Overlay: Attach escape handler to overlay container"

src/__tests__/Overlay.test.tsx

Lines changed: 0 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -56,32 +56,6 @@ const TestComponent = ({initialFocus, callback}: TestComponentSettings) => {
5656
)
5757
}
5858

59-
// https://github.com/primer/react/issues/1802
60-
const BugRepro1802 = ({mockHandler}: {mockHandler: (event: KeyboardEvent) => void}) => {
61-
const [isOpen, setIsOpen] = useState(false)
62-
const buttonRef = useRef<HTMLButtonElement>(null)
63-
64-
React.useEffect(() => {
65-
document.addEventListener('keydown', mockHandler)
66-
return () => document.removeEventListener('keydown', mockHandler)
67-
}, [mockHandler])
68-
69-
return (
70-
<ThemeProvider theme={theme}>
71-
<BaseStyles>
72-
<Button ref={buttonRef} onClick={() => setIsOpen(!isOpen)}>
73-
open overlay
74-
</Button>
75-
{isOpen ? (
76-
<Overlay returnFocusRef={buttonRef} onEscape={() => setIsOpen(false)} onClickOutside={() => setIsOpen(false)}>
77-
<Text>Text inside Overlay</Text>
78-
</Overlay>
79-
) : null}
80-
</BaseStyles>
81-
</ThemeProvider>
82-
)
83-
}
84-
8559
describe('Overlay', () => {
8660
it('should have no axe violations', async () => {
8761
const {container} = render(<TestComponent />)
@@ -126,14 +100,4 @@ describe('Overlay', () => {
126100
const cancelButtons = queryAllByText('Cancel')
127101
expect(cancelButtons).toHaveLength(0)
128102
})
129-
130-
it('should call stop propagation', () => {
131-
const mockHandler = jest.fn()
132-
const {getByText} = render(<BugRepro1802 mockHandler={mockHandler} />)
133-
act(() => userEvent.click(getByText('open overlay')))
134-
const domNode = getByText('Text inside Overlay')
135-
fireEvent.keyDown(domNode, {key: 'Escape', code: 'Escape', keyCode: 27, charCode: 27})
136-
// if stopPropagation worked, mockHandler would not have been called
137-
expect(mockHandler).toHaveBeenCalledTimes(0)
138-
})
139103
})

src/hooks/useOnEscapePress.ts

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const handlers: ((e: KeyboardEvent) => void)[] = []
99
function handleEscape(event: KeyboardEvent) {
1010
if (event.key === 'Escape' && !event.defaultPrevented) {
1111
for (let i = handlers.length - 1; i >= 0; --i) {
12-
if (typeof handlers[i] === 'function') handlers[i](event)
12+
handlers[i](event)
1313
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
1414
if (event.defaultPrevented) {
1515
break
@@ -19,7 +19,7 @@ function handleEscape(event: KeyboardEvent) {
1919
}
2020

2121
/**
22-
* Sets up a `keydown` listener on element passed | `window.document`. If
22+
* Sets up a `keydown` listener on `window.document`. If
2323
* 1) The pressed key is "Escape", and
2424
* 2) The event has not had `.preventDefault()` called
2525
* The given callback will be executed.
@@ -38,24 +38,16 @@ function handleEscape(event: KeyboardEvent) {
3838
* @param callbackDependencies {React.DependencyList} The dependencies of the given
3939
* `onEscape` callback for memoization. Omit this param if the callback is already
4040
* memoized. See `React.useCallback` for more info on memoization.
41-
*
42-
* @param containerRef {React.RefObject<HTMLElement>} The overlay element to attach the
43-
* handlers on. If not provided, fallback to document.
4441
*/
4542
export const useOnEscapePress = (
4643
onEscape: (e: KeyboardEvent) => void,
47-
callbackDependencies: React.DependencyList = [onEscape],
48-
containerRef?: React.RefObject<HTMLElement>
44+
callbackDependencies: React.DependencyList = [onEscape]
4945
): void => {
5046
// eslint-disable-next-line react-hooks/exhaustive-deps
5147
const escapeCallback = useCallback(onEscape, callbackDependencies)
52-
5348
useEffect(() => {
54-
const element = containerRef?.current
55-
5649
if (handlers.length === 0) {
57-
if (element) element.addEventListener('keydown', handleEscape)
58-
else document.addEventListener('keydown', handleEscape)
50+
document.addEventListener('keydown', handleEscape)
5951
}
6052
handlers.push(escapeCallback)
6153
return () => {
@@ -64,9 +56,8 @@ export const useOnEscapePress = (
6456
1
6557
)
6658
if (handlers.length === 0) {
67-
if (element) element.removeEventListener('keydown', handleEscape)
68-
else document.removeEventListener('keydown', handleEscape)
59+
document.removeEventListener('keydown', handleEscape)
6960
}
7061
}
71-
}, [escapeCallback, containerRef])
62+
}, [escapeCallback])
7263
}

src/hooks/useOverlay.tsx

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,12 +29,6 @@ export const useOverlay = ({
2929
const overlayRef = useProvidedRefOrCreate<HTMLDivElement>(_overlayRef)
3030
useOpenAndCloseFocus({containerRef: overlayRef, returnFocusRef, initialFocusRef, preventFocusOnOpen})
3131
useOnOutsideClick({containerRef: overlayRef, ignoreClickRefs, onClickOutside})
32-
33-
/** Don't bubble Escape event up the tree */
34-
const onEscapeWithStoppedPropagation: UseOverlaySettings['onEscape'] = event => {
35-
event.stopPropagation()
36-
onEscape(event)
37-
}
38-
useOnEscapePress(onEscapeWithStoppedPropagation, undefined, overlayRef)
32+
useOnEscapePress(onEscape)
3933
return {ref: overlayRef}
4034
}

src/stories/Overlay.stories.tsx

Lines changed: 1 addition & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,7 @@
11
import React, {useState, useRef, useCallback} from 'react'
2-
import ReactDOM from 'react-dom'
32
import {Meta} from '@storybook/react'
43
import styled from 'styled-components'
5-
import {TriangleDownIcon, PlusIcon} from '@primer/octicons-react'
6-
import {
7-
BaseStyles,
8-
Overlay,
9-
Button,
10-
ButtonInvisible,
11-
ButtonPrimary,
12-
ButtonGroup,
13-
Text,
14-
ButtonDanger,
15-
ThemeProvider,
16-
Box,
17-
StyledOcticon,
18-
Checkbox,
19-
ChoiceInputField,
20-
TextInput,
21-
ActionList
22-
} from '..'
4+
import {BaseStyles, Overlay, Button, Text, ButtonDanger, ThemeProvider, Box} from '..'
235
import type {AnchorSide} from '@primer/behaviors'
246
import {DropdownMenu, DropdownButton} from '../DropdownMenu'
257
import {ItemInput} from '../ActionList/List'
@@ -229,97 +211,3 @@ export const OverlayOnTopOfOverlay = ({anchorSide}: OverlayProps) => {
229211
</Box>
230212
)
231213
}
232-
233-
export const NestedOverlays = () => {
234-
const [listOverlayOpen, setListOverlayOpen] = React.useState(false)
235-
const [createListOverlayOpen, setCreateListOverlayOpen] = React.useState(false)
236-
237-
const buttonRef = useRef<HTMLButtonElement>(null)
238-
const secondaryButtonRef = useRef<HTMLButtonElement>(null)
239-
240-
React.useEffect(() => {
241-
// eslint-disable-next-line no-console
242-
const handler = (event: KeyboardEvent) => console.log('global handler:', event.key)
243-
document.addEventListener('keydown', handler)
244-
return () => document.removeEventListener('keydown', handler)
245-
}, [])
246-
247-
const hostElement = document.createElement('div')
248-
ReactDOM.render(<div>hello</div>, hostElement)
249-
250-
return (
251-
<div>
252-
<TextInput />
253-
<ButtonGroup display="block" my={2}>
254-
<Button>Star</Button>
255-
<Button
256-
aria-label="Add this repository to a list"
257-
ref={buttonRef}
258-
onClick={() => setListOverlayOpen(!listOverlayOpen)}
259-
sx={{paddingX: 2}}
260-
>
261-
<TriangleDownIcon />
262-
</Button>
263-
</ButtonGroup>
264-
{listOverlayOpen && (
265-
<Overlay
266-
width="medium"
267-
onEscape={() => setListOverlayOpen(false)}
268-
onClickOutside={() => setListOverlayOpen(false)}
269-
returnFocusRef={buttonRef}
270-
ignoreClickRefs={[buttonRef]}
271-
top={60}
272-
left={16}
273-
>
274-
<Box sx={{display: 'flex', flexDirection: 'column', py: 2}}>
275-
<Box sx={{paddingX: 3}}>
276-
<Text color="fg.muted" sx={{fontSize: 1}}>
277-
Add to list
278-
</Text>
279-
<Box sx={{marginY: 1}}>
280-
<ChoiceInputField>
281-
<ChoiceInputField.Label>My stack</ChoiceInputField.Label>
282-
<Checkbox />
283-
</ChoiceInputField>
284-
<ChoiceInputField>
285-
<ChoiceInputField.Label>Want to try</ChoiceInputField.Label>
286-
<Checkbox />
287-
</ChoiceInputField>
288-
</Box>
289-
</Box>
290-
<ActionList.Divider />
291-
<ButtonInvisible
292-
ref={secondaryButtonRef}
293-
sx={{textAlign: 'left', px: 2, mx: 2}}
294-
onClick={() => setCreateListOverlayOpen(!createListOverlayOpen)}
295-
>
296-
<StyledOcticon icon={PlusIcon} sx={{mr: 1}} />
297-
Create list
298-
</ButtonInvisible>
299-
</Box>
300-
{createListOverlayOpen && (
301-
<Overlay
302-
width="large"
303-
onEscape={() => setCreateListOverlayOpen(false)}
304-
onClickOutside={() => setCreateListOverlayOpen(false)}
305-
returnFocusRef={secondaryButtonRef}
306-
ignoreClickRefs={[secondaryButtonRef]}
307-
top={120}
308-
left={64}
309-
>
310-
<Box as="form" sx={{display: 'flex', flexDirection: 'column', p: 3}}>
311-
<Text color="fg.muted" sx={{fontSize: 1, mb: 3}}>
312-
Create a list to organize your starred repositories.
313-
</Text>
314-
<TextInput placeholder="Name this list" sx={{mb: 2}} />
315-
<TextInput as="textarea" placeholder="Write a description" rows="3" sx={{mb: 2, textarea: {p: 2}}} />
316-
317-
<ButtonPrimary onClick={() => setCreateListOverlayOpen(!createListOverlayOpen)}>Create</ButtonPrimary>
318-
</Box>
319-
</Overlay>
320-
)}
321-
</Overlay>
322-
)}
323-
</div>
324-
)
325-
}

0 commit comments

Comments
 (0)