Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting keyboard navigation to work with MUI Autocomplete and SimpleBar for react

I am trying to add Simplebar scrollbar to the MUI Material Autocomplete component, instead of the default browser one. All works but doing that I've lost the ability to navigate the options list with the keyboard.

There is this snippet from the MUI docs

ListboxComponent If you provide a custom ListboxComponent prop, you need to make sure that the intended scroll container has the role attribute set to listbox. This ensures the correct behavior of the scroll, for example when using the keyboard to navigate.

But I have no idea how to do that.

The following code is from the MUI docs, first autocomplete example with custom ListboxComponenet and shortened movie list. (https://mui.com/components/autocomplete/)

import * as React from 'react';
import TextField from '@mui/material/TextField';
import Autocomplete from '@mui/material/Autocomplete';

import SimpleBar from "simplebar-react";
import "simplebar/dist/simplebar.min.css";

export default function ComboBox() {
  return (
    <Autocomplete
      disablePortal
      id="combo-box-demo"
      options={top100Films}
      ListboxComponent={SimpleBar}
      sx={{ width: 300 }}
      renderInput={(params) => <TextField {...params} label="Movie" />}
    />
  );
}

// Top 100 films as rated by IMDb users. http://www.imdb.com/chart/top
const top100Films = [
  { label: 'City of God', year: 2002 },
  { label: 'Se7en', year: 1995 },
  { label: 'The Silence of the Lambs', year: 1991 },
  { label: "It's a Wonderful Life", year: 1946 },
  { label: 'Life Is Beautiful', year: 1997 },
  { label: 'The Usual Suspects', year: 1995 },
  { label: 'LΓ©on: The Professional', year: 1994 },
  { label: 'Spirited Away', year: 2001 },
  { label: 'Saving Private Ryan', year: 1998 },
  { label: 'Once Upon a Time in the West', year: 1968 },
  { label: 'American History X', year: 1998 },
  { label: 'Interstellar', year: 2014 },
  { label: 'Casablanca', year: 1942 },
  { label: 'City Lights', year: 1931 },
  { label: 'Psycho', year: 1960 },
  { label: 'The Green Mile', year: 1999 },
  { label: 'The Intouchables', year: 2011 },
  { label: 'Modern Times', year: 1936 },
  { label: 'Raiders of the Lost Ark', year: 1981 },
  { label: 'Rear Window', year: 1954 },
  { label: 'The Pianist', year: 2002 },
  { label: 'The Departed', year: 2006 },
  { label: 'Terminator 2: Judgment Day', year: 1991 },
  { label: 'Back to the Future', year: 1985 },
  { label: 'Whiplash', year: 2014 },
  { label: 'Gladiator', year: 2000 },
  { label: 'Memento', year: 2000 },
];

I have also tried the following but that also doesn't seem to work.

ListboxComponent={(props) => <SimpleBar {...props} role="listbox" />}

Any help would be appreciated, thanks.

like image 504
Sead Avatar asked Nov 06 '22 23:11

Sead


1 Answers

The problem is actually very complicated. Looking at its implementation, <SimpleBar /> doesn't pass either the React ref or the role prop to the correct element. The correct element I believe is .scrollbar-content, which is very deeply nested and basically untouchable.

ETA: In case you thought of getting cheesy with document.querySelectorAll setAttribute shenanigans, that will not work. The ref also needs to point at the correct element, and I don't think that's codeable on the workspace side.

The cleanest solution I can think of is to use Yarn 3 (πŸ‘) and patch simplebar-react yourself, passing the needed props to .scrollbar-content. Then you do:

const ListboxSimpleBar = React.forwardRef(function ListboxSimpleBar(props, ref) {
  return <SimpleBar {...props} role='listbox' listboxRef={ref} />
}

// ...
ListboxComponent={ListboxSimpleBar}

The less clean solution is to fork the repo, patch it, and install it as a git dependency. Or publish to npm once you're sure it works and install as a regular dependency. That's the recommended method if you don't use Yarn 3 (πŸ‘Ž). It's much more tedious and won't receive updates, but it is an option that exists.

The least clean solution is to copypasterino the simplebar-react code in your own workspace, edit it and import it in lieu of the real simplebar-react. Hackier, messier and funnier option 2, but only just barely.

like image 124
Summer Avatar answered Nov 11 '22 02:11

Summer