Skip to content

Commit

Permalink
feat: add keydown functionality to menu and menuitem components
Browse files Browse the repository at this point in the history
  • Loading branch information
d-rita committed Feb 19, 2024
1 parent 83d74de commit b06d6d2
Show file tree
Hide file tree
Showing 2 changed files with 89 additions and 36 deletions.
11 changes: 10 additions & 1 deletion components/menu/src/menu-item/menu-item.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { Portal } from '@dhis2-ui/portal'
import { IconChevronRight24 } from '@dhis2/ui-icons'
import cx from 'classnames'
import PropTypes from 'prop-types'
import React, { useRef } from 'react'
import React, { useEffect, useRef } from 'react'
import { FlyoutMenu } from '../index.js'
import styles from './menu-item.styles.js'

Expand Down Expand Up @@ -41,9 +41,16 @@ const MenuItem = ({
toggleSubMenu,
suffix,
checkbox,
handleRef,
}) => {
const menuItemRef = useRef()

useEffect(() => {
if (handleRef) {
handleRef(menuItemRef.current)
}
}, [handleRef])

return (
<>
<li
Expand All @@ -59,6 +66,7 @@ const MenuItem = ({
role={checkbox ? 'menuitemcheckbox' : 'menuitem'}
aria-haspopup={toggleSubMenu ? 'menu' : undefined}
aria-disabled={disabled}
aria-pressed={active}
>
<a
target={target}
Expand Down Expand Up @@ -118,6 +126,7 @@ MenuItem.propTypes = {
dense: PropTypes.bool,
destructive: PropTypes.bool,
disabled: PropTypes.bool,
handleRef: PropTypes.func,
/** For using menu item as a link */
href: PropTypes.string,
/** An icon for the left side of the menu item */
Expand Down
114 changes: 79 additions & 35 deletions components/menu/src/menu/menu.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,83 @@
import PropTypes from 'prop-types'
import React, { Children, cloneElement, isValidElement } from 'react'

const Menu = ({ children, className, dataTest, dense }) => (
<ul className={className} data-test={dataTest} tabIndex={0} role="menu">
{Children.map(
children,
(child, index) =>
isValidElement(child)
? cloneElement(child, {
dense:
typeof child.props.dense === 'boolean'
? child.props.dense
: dense,
hideDivider:
typeof child.props.hideDivider !== 'boolean' &&
index === 0
? true
: child.props.hideDivider,
})
: child // to do: role = menuitem
)}

<style jsx>{`
ul {
display: block;
position: relative;
width: 100%;
margin: 0;
padding: 0;
user-select: none;
}
`}</style>
</ul>
)
import React, { Children, cloneElement, isValidElement, useState } from 'react'

const Menu = ({ children, className, dataTest, dense }) => {
const [selectedIndex, setSelectedIndex] = useState(0)

const handleActionKeys = () => {
console.log('Action keys: Space and Enter')
}

const handleDirectionKeys = (key) => {
if (key === 'ArrowUp') {
setSelectedIndex((index) =>
index !== 0 ? index - 1 : children.length - 1
)
}
if (key === 'ArrowDown') {
setSelectedIndex((index) =>
index !== children.length - 1 ? index + 1 : 0
)
}
}

const handleKeyDown = (event) => {
event.preventDefault()
if (event.key === ' ' || event.key === 'Enter') {
handleActionKeys(event.key)
}
if (event.key === 'ArrowUp' || event.key === 'ArrowDown') {
handleDirectionKeys(event.key)
}
}

return (
<ul
className={className}
data-test={dataTest}
tabIndex={0}
role="menu"
onKeyDown={handleKeyDown}
>
{Children.map(
children,
(child, index) =>
isValidElement(child)
? cloneElement(child, {
dense:
typeof child.props.dense === 'boolean'
? child.props.dense
: dense,
hideDivider:
typeof child.props.hideDivider !==
'boolean' && index === 0
? true
: child.props.hideDivider,
active: index === selectedIndex,
handleRef: (ref) => {
if (index === selectedIndex && ref) {
console.log(ref)
ref.focus()
}
},
})
: child // to do: role = menuitem
)}

<style jsx>{`
ul {
display: block;
position: relative;
width: 100%;
margin: 0;
padding: 0;
user-select: none;
}
`}</style>
</ul>
)
}

Menu.defaultProps = {
dataTest: 'dhis2-uicore-menulist',
Expand Down

0 comments on commit b06d6d2

Please sign in to comment.