DraftJS: Mentions Plugin with Scrolling and Keyboard Navigation

Solving the problem of a scrolling results list with the DraftJS mentions plugin.

A good while back I had to solve an issue where we were using the Mention plugin for DraftJS.

We wanted to have a longer list of mention suggestions, and manage the overflow by letting the user scroll the list. This idea itself isn’t a big issue, but what if you were using the keyboard to navigate?

This is where a custom <Entry /> component was required. When using your custom component you get access to a collection of props such as mouse handlers, the mention object itself, and what we’re interested in, which is isFocused.

With isFocused we can trigger a call to scroll the focused option into view. We can do this by attaching a ref to our custom component, and with useEffect being called when the focus changes, we can call one of two appropriate methods:

scrollIntoViewIfNeeded is non-standard, and has limited support. It does offer the expected behaviour, though, both for moving up and down the list.

scrollIntoView is more widely supported, and offers us expected behaviour moving down the list, but not up.

import React, { useRef, useEffect } from "react";

export default function Entry({
  mention,
  isFocused,
  id,
  onMouseUp,
  onMouseDown,
  onMouseEnter
}) {
  const entryRef = useRef(null);
  let className = "mention-text";

  if (isFocused) {
    className += " mention-focused";
  }

  useEffect(() => {
    if (isFocused) {
      if ("scrollIntoViewIfNeeded" in document.body) {
        entryRef.current.scrollIntoViewIfNeeded(false);
      } else {
        entryRef.current.scrollIntoView(false);
      }
    }
  }, [isFocused]);

  return (
    <div
      ref={entryRef}
      className={className}
      role="option"
      aria-selected={isFocused ? "true" : "false"}
      id={id}
      onMouseUp={onMouseUp}
      onMouseEnter={onMouseEnter}
      onMouseDown={onMouseDown}
    >
      {mention.name}
    </div>
  );
}

Demo