Skip to content

Commit

Permalink
Trap focus in AnchoredOverlay as soon as it opens (#1222)
Browse files Browse the repository at this point in the history
  • Loading branch information
dgreif committed May 12, 2021
1 parent d78af59 commit 4ab3d17
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 30 deletions.
5 changes: 5 additions & 0 deletions .changeset/silver-scissors-love.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@primer/components": patch
---

Trap focus in `AnchoredOverlay` as soon as it opens, regardless of the event that triggered it to open
36 changes: 6 additions & 30 deletions src/AnchoredOverlay/AnchoredOverlay.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react'
import React, {useCallback, useMemo, useRef} from 'react'
import Overlay, {OverlayProps} from '../Overlay'
import {useFocusTrap} from '../hooks/useFocusTrap'
import {useFocusZone} from '../hooks/useFocusZone'
Expand Down Expand Up @@ -49,49 +49,26 @@ export const AnchoredOverlay: React.FC<AnchoredOverlayProps> = ({
}) => {
const anchorRef = useRef<HTMLElement>(null)
const [overlayRef, updateOverlayRef] = useRenderForcingRef<HTMLDivElement>()
const [focusType, setFocusType] = useState<null | 'anchor' | 'list'>(open ? 'list' : null)
const anchorId = useMemo(uniqueId, [])

const onClickOutside = useCallback(() => onClose?.('click-outside'), [onClose])
const onEscape = useCallback(() => onClose?.('escape'), [onClose])

useEffect(() => {
if (!open) {
setFocusType(null)
}
}, [open])

const onAnchorKeyDown = useCallback(
(event: React.KeyboardEvent<HTMLElement>) => {
if (!event.defaultPrevented) {
if (!open) {
if (['ArrowDown', 'ArrowUp'].includes(event.key)) {
setFocusType('list')
onOpen?.('anchor-key-press')
event.preventDefault()
} else if ([' ', 'Enter'].includes(event.key)) {
setFocusType('anchor')
onOpen?.('anchor-key-press')
event.preventDefault()
}
} else if (focusType === 'anchor') {
if (['ArrowDown', 'ArrowUp', 'Tab', 'Enter'].includes(event.key)) {
setFocusType('list')
event.preventDefault()
} else if (event.key === 'Escape') {
onClose?.('escape')
event.preventDefault()
}
if (!open && ['ArrowDown', 'ArrowUp', ' ', 'Enter'].includes(event.key)) {
onOpen?.('anchor-key-press')
event.preventDefault()
}
}
},
[open, focusType, onOpen, onClose]
[open, onOpen]
)
const onAnchorClick = useCallback(
(event: React.MouseEvent<HTMLElement>) => {
if (!event.defaultPrevented && event.button === 0 && !open) {
onOpen?.('anchor-click')
setFocusType('anchor')
}
},
[open, onOpen]
Expand All @@ -109,7 +86,7 @@ export const AnchoredOverlay: React.FC<AnchoredOverlayProps> = ({
}, [position])

useFocusZone({containerRef: overlayRef, disabled: !open || !position})
useFocusTrap({containerRef: overlayRef, disabled: !open || focusType !== 'list' || !position})
useFocusTrap({containerRef: overlayRef, disabled: !open || !position})

return (
<>
Expand All @@ -124,7 +101,6 @@ export const AnchoredOverlay: React.FC<AnchoredOverlayProps> = ({
})}
{open ? (
<Overlay
initialFocusRef={anchorRef}
returnFocusRef={anchorRef}
onClickOutside={onClickOutside}
onEscape={onEscape}
Expand Down

1 comment on commit 4ab3d17

@vercel
Copy link

@vercel vercel bot commented on 4ab3d17 May 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.