Skip to content

Commit

Permalink
feat: enable keyboard navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
jamil314 committed Sep 20, 2024
1 parent 6853f29 commit 6e39150
Showing 1 changed file with 47 additions and 5 deletions.
52 changes: 47 additions & 5 deletions packages/components/src/ToggleMenu/ToggleMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import styled from 'styled-components'
import React, { useEffect, useRef, useState } from 'react'
import { Button } from '../Button'
import { noop } from 'lodash'
import { disabled } from '../Button/Button.styles'

const ToggleMenuContainer = styled.nav`
Expand Down Expand Up @@ -113,6 +112,9 @@ export const ToggleMenu = ({
hide
}: IProps) => {
const [showSubmenu, setShowSubmenu] = useState(false)
const [focusedIndex, setFocusedIndex] = useState<number | null>(null)
const menuRef = useRef<HTMLUListElement>(null)
const itemRefs = useRef<(HTMLLIElement | null)[]>([])

const closeMenuRef = useRef(() => {
setShowSubmenu(false)
Expand All @@ -124,13 +126,43 @@ export const ToggleMenu = ({
}
})

const getNextIndex = (last: number) => {
let nextIndex = (last ?? -1) + 1
while (nextIndex < menuItems.length && menuItems[nextIndex].isDisabled) {
nextIndex++
}
return nextIndex < menuItems.length ? nextIndex : last
}

const getPreviousIndex = (last: number) => {
let prevIndex = (last ?? menuItems.length) - 1
while (prevIndex >= 0 && menuItems[prevIndex].isDisabled) {
prevIndex--
}
return prevIndex >= 0 ? prevIndex : last
}

const handleKeyUp = (e: React.KeyboardEvent) => {
if (e.key === 'ArrowDown') {
setFocusedIndex(getNextIndex)
} else if (e.key === 'ArrowUp') {
setFocusedIndex(getPreviousIndex)
} else if ((e.key === 'Enter' || e.key === ' ') && focusedIndex !== null) {
menuItems[focusedIndex].handler()
setShowSubmenu(false)
}
}

useEffect(() => {
const closeMenu = closeMenuRef.current
const closeMenuOnEscape = closeMenuOnEscapeRef.current
if (showSubmenu) {
menuRef.current?.focus()
//https://github.com/facebook/react/issues/24657#issuecomment-1150119055
setTimeout(() => document.addEventListener('click', closeMenu), 0)
setTimeout(() => document.addEventListener('keyup', closeMenuOnEscape), 0)
} else {
setFocusedIndex(null)
}

return () => {
Expand All @@ -139,6 +171,12 @@ export const ToggleMenu = ({
}
}, [showSubmenu])

useEffect(() => {
if (focusedIndex !== null && itemRefs.current[focusedIndex]) {
itemRefs.current[focusedIndex]?.focus()
}
}, [focusedIndex])

const toggleMenu = () => {
setShowSubmenu(!showSubmenu)
}
Expand All @@ -159,16 +197,20 @@ export const ToggleMenu = ({
{toggleButton}
</Button>
{showSubmenu && (
<MenuContainer id={`${id}SubMenu`}>
<MenuContainer
id={`${id}SubMenu`}
ref={menuRef}
tabIndex={0}
onKeyUp={handleKeyUp}
>
{menuHeader && <MenuHeader>{menuHeader}</MenuHeader>}
{menuItems.map((mi: IToggleMenuItem, index) => (
<MenuItem
id={`${id}Item${index}`}
key={`${id}-${index}`}
ref={(el) => (itemRefs.current[index] = el)}
onFocus={() => setFocusedIndex(index)}
onClick={mi.handler}
onKeyUp={(e) =>
e.key === 'Enter' || e.key === ' ' ? mi.handler() : noop
}
tabIndex={mi.isDisabled ? -1 : 0}
role="button"
disabled={mi.isDisabled}
Expand Down

0 comments on commit 6e39150

Please sign in to comment.