pacocoursey / cmdk

Fast, unstyled command menu React component.
https://cmdk.paco.me
MIT License
9.98k stars 287 forks source link

Unaccessible keyboard navigation through items when no CommandInput #322

Open crissadriana opened 1 month ago

crissadriana commented 1 month ago

Hi,

I'm facing issues with the keyboard navigation through the command items if I don't include the CommandInput. Basically I need the entire functionality of a combobox but in some cases, there's no need for Search input (eg. design requires when there are 3 items only, no need to search through them). When I interact with the items with the mouse (hover / click) all is good, but if I attempt to navigate by keyboard only, it just doesn't work. When opening the command, the first item seems to be "selected=true" but when pressing down arrow it doesn't move to the next item. It seems like it's a focus issue, because if I click the scrollbar of the combobox and then I start again using the up/down arrows, it works.

This is a basic implementation (using shadcnui):

export function ComboboxDemo() {
  const [open, setOpen] = useState(false);
  const [value, setValue] = useState("");

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <PopoverTrigger asChild>
        <button
          role="combobox"
          aria-expanded={open}
          className="w-[200px] justify-between"
        >
          {value
            ? frameworks.find((framework) => framework.value === value)?.label
            : "Select framework..."}
        </button>
      </PopoverTrigger>
      <PopoverContent className="w-[200px] p-0" >
        <Command>
          {/* Hide search bar for now */}
          {/* <CommandInput placeholder="Search framework..." /> */}
          <CommandList
          >
            <CommandEmpty>No framework found.</CommandEmpty>
            <CommandGroup >
              {frameworks.map((framework) => (
                <CommandItem
                  key={framework.value}
                  value={framework.value}
                  onSelect={(currentValue) => {
                    setValue(currentValue === value ? "" : currentValue);
                    setOpen(false);
                  }}
                >
                  <IconCheck
                    className={cn(
                      "mr-2 h-4 w-4",
                      value === framework.value ? "opacity-100" : "opacity-0",
                    )}
                  />
                  {framework.label}
                </CommandItem>
              ))}
            </CommandGroup>
          </CommandList>
        </Command>
      </PopoverContent>
    </Popover>
  );
}

Any suggestion would be highly appreciated!

CyrilOlanolan commented 3 weeks ago

Encountered this problem as well. As a temporary solution, I added a tabIndex={0} in <CommandItem/>.

You can now use enter to select but not space. I tried overriding it using the onKeyDown attribute but couldn't get it to work. I also think it's a focus problem.

{options.map((option, index) => (
  <CommandItem
    key={`${index}_${option.value}`}
    tabIndex={0}
    onSelect={() => handleSelect(option)}
  >
    {option.label}
  </CommandItem>
))}
crissadriana commented 3 weeks ago

I've also came with an workaround (in case it'll be useful to anyone): when no search input, added a custom button auto focused which would be hidden for screen readers but will initiate the focus in the combobox list

<Command>
  {search ? (<CommandInput />) : (<button autoFocus aria-hidden="true" className="sr-only" />)}
  <CommandList>....</CommandList>
</Command>