Skip to content

Commit

Permalink
wip feat(overlay-dropdown): add keyboard navigation
Browse files Browse the repository at this point in the history
  • Loading branch information
Niklas Kiefer committed Dec 10, 2021
1 parent 9cf9553 commit edcb354
Show file tree
Hide file tree
Showing 2 changed files with 73 additions and 2 deletions.
73 changes: 72 additions & 1 deletion client/src/shared/ui/overlay/OverlayDropdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,9 +132,11 @@ function OptionGroup(props) {
function Options(props) {
const { items, onSelect } = props;

const focusRef = useArrowNavigation();

return (
<Section.Body>
<ul>
<ul ref={ focusRef }>
{
items.map((item, index) =>
<Option
Expand Down Expand Up @@ -171,4 +173,73 @@ function Option(props) {

function isGrouped(items) {
return items.length && items[0].key;
}

function useArrowNavigation() {
const ref = React.createRef(0);

const handleKeydown = (event) => {
const {
key,
keyCode,
currentTarget
} = event;

if (key === 'ArrowDown' && keyCode == 40) {
focusNext(currentTarget);
} else if (key === 'ArrowUp' && keyCode == 38) {
focusPrevious(currentTarget);
}
};

React.useEffect(() => {
if (!ref.current) {
return;
}

const items = ref.current.querySelectorAll('li');

items.forEach(i => i.addEventListener('keydown', handleKeydown));

return () => {
items.forEach(i => i.removeEventListener('keydown', handleKeydown));
};

}, [ ref.current ]);

return ref;
}

/**
*
* @param {Node} focusElement
*/
function focusNext(focusElement) {
const nextSibling = focusElement.nextSibling;

// (1) focus immediate neighbor
if (nextSibling) {
return nextSibling.querySelector('button').focus();
}

// (2) try to find neighbor in other section
const nextSection = focusElement.closest('section').nextElementSibling;
return nextSection && nextSection.querySelector('li button').focus();
}

/**
*
* @param {Node} focusElement
*/
function focusPrevious(focusElement) {
const previousSibling = focusElement.previousSibling;

// (1) focus immediate neighbor
if (previousSibling) {
return previousSibling.querySelector('button').focus();
}

// (2) try to find neighbor in other section
const previousSection = focusElement.closest('section').previousElementSibling;
return previousSection && previousSection.querySelector('li:last-child button').focus();
}
2 changes: 1 addition & 1 deletion client/src/shared/ui/overlay/OverlayDropdown.less
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
background-color: var(--overlay-dropdown-option-hover-background-color);
}

li button:focus-visible {
li button:focus {
background-color: var(--overlay-dropdown-focus-hover-background-color);
outline: none;
box-shadow: none;
Expand Down

0 comments on commit edcb354

Please sign in to comment.