shadcn-ui/ui

[bug]: Combo box is not working.

akeakun opened this issue ยท 12 comments

Describe the bug

the default code for combobox in a next.js app throws this error:

Unhandled Runtime Error
TypeError: undefined is not iterable (cannot read property Symbol(Symbol.iterator))

Affected component/components

ComboBox

How to reproduce

just copy the default code for combo box from shadcnui.com/combobox and paste it in page.tsx
run the code and the error should pop up while trying to click the PopoverTrigger button.

Codesandbox/StackBlitz link

No response

Logs

No response

System Info

browser

Before submitting

  • I've made research efforts and searched the documentation
  • I've searched for existing issues

me too. I use the example code in the popover anchor, maybe we should add the CommandList comp:
image

maybe the cmdk package has some changes and the shadcnui not response to.

you need to use this version, v1 doesnt work

    "cmdk": "^0.2.1",

or

tailwind.config.ts

  plugins: [
    require("tailwindcss-animate"),
    function ({ addUtilities }) {
      addUtilities({
        ".popover-content-width-same-as-its-trigger": {
          width: "var(--radix-popover-trigger-width)",
          "max-height": "var(--radix-popover-content-available-height)",
        },
      });
    },
  ],

my-combobox.tsx

type MyComboboxProps = {
  label?: string;
  value: string;
  setValue: (value: string) => void;
  values: { label: string; value: string }[];
  inputDisabled?: boolean;
  inputPlaceholder?: string;
};

export const MyCombobox: React.FC<MyComboboxProps> = (props) => {
  const [open, setOpen] = useState(false);

  return (
    <Popover open={open} onOpenChange={setOpen}>
      <div className="flex flex-col w-full">
        {props.label && <Label className="mb-1">{props.label}</Label>}
        <PopoverTrigger asChild>
          <Button
            variant="outline"
            role="combobox"
            aria-expanded={open}
            className=" justify-between"
          >
            {props.value
              ? props.values.find((item) => item.value === props.value)?.label
              : props.label + "..."}
            <ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
          </Button>
        </PopoverTrigger>
      </div>
      <PopoverContent
        className={"popover-content-width-same-as-its-trigger p-0"}
      >
        <Command>
          {props.inputDisabled && (
            <CommandInput placeholder={props.inputPlaceholder} />
          )}
          <CommandEmpty>NOTHING_FOUND</CommandEmpty>
          <CommandGroup>
            <CommandList>
              {props.values.map((item) => (
                <CommandItem
                  key={item.value}
                  value={item.value}
                  onSelect={(currentValue) => {
                    props.setValue(
                      currentValue === props.value ? "" : currentValue
                    );
                    setOpen(false);
                  }}
                >
                  <Check
                    className={cn(
                      "mr-2 h-4 w-4",
                      props.value === item.value ? "opacity-100" : "opacity-0"
                    )}
                  />
                  {item.label}
                </CommandItem>
              ))}
            </CommandList>
          </CommandGroup>
        </Command>
      </PopoverContent>
    </Popover>
  );
};

you need to use this version, v1 doesnt work

    "cmdk": "^0.2.1",

Great catch, once downgrading I no longer run into the issue.

Just wrap CommandGroup in a CommandList and it will work on latest cmdk. Move the CommandEmpty inside as well.

It should look like this: (just command, popover stuff removed for brevity)

      <Command>
        <CommandInput placeholder="Search ..." />
        <CommandList>
          <CommandEmpty>No option found.</CommandEmpty>
          <CommandGroup>
            {options.map((option) => {
              return (
                <CommandItem
                  key={option.value}
                  value={option.value}
                  onSelect={(currentValue) => {
                    setValue(currentValue === value ? "" : currentValue);
                    setOpen(false);
                    onValueChange(currentValue);
                  }}
                >
                  <CheckIcon
                    className={cn(
                      "mr-2 h-4 w-4",
                      value === option.value ? "opacity-100" : "opacity-0",
                    )}
                  />
                  {option.label}
                </CommandItem>
              );
            })}
          </CommandGroup>
        </CommandList>
      </Command>

you need to use this version, v1 doesnt work

    "cmdk": "^0.2.1",

Weekend saved, nice catch

Yo bro, the command box may not be working for 2 reasons.

  1. You didnt wrap the CommantGroup or CommandItem with the CommandList, its required with the current relaease as of this date.
  2. The exact issue i had struggled with for over 6hours. Go to your Command component, and paste the code below to overwrite the default. Or simply replace every className with data-[disabled] to data-[disabled=true].

"use client";

import * as React from "react";
import { type DialogProps } from "@radix-ui/react-dialog";
import { MagnifyingGlassIcon } from "@radix-ui/react-icons";
import { Command as CommandPrimitive } from "cmdk";

import { cn } from "@/lib/utils";
import { Dialog, DialogContent } from "@/components/ui/dialog";

const Command = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef

(({ className, ...props }, ref) => (
<CommandPrimitive
ref={ref}
className={cn(
"flex h-full w-full flex-col overflow-hidden rounded-md bg-popover text-popover-foreground",
className
)}
{...props}
/>
));
Command.displayName = CommandPrimitive.displayName;

interface CommandDialogProps extends DialogProps {}

const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
return (
<Dialog {...props}>


{children}



);
};

const CommandInput = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef

(({ className, ...props }, ref) => (

));

CommandInput.displayName = CommandPrimitive.Input.displayName;

const CommandList = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef

(({ className, ...props }, ref) => (
<CommandPrimitive.List
ref={ref}
className={cn(
"max-h-[300px] overflow-y-auto overflow-x-hidden",
className
)}
{...props}
/>
));

CommandList.displayName = CommandPrimitive.List.displayName;

const CommandEmpty = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef

((props, ref) => (
<CommandPrimitive.Empty
ref={ref}
className="py-6 text-center text-sm"
{...props}
/>
));

CommandEmpty.displayName = CommandPrimitive.Empty.displayName;

const CommandGroup = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef

(({ className, ...props }, ref) => (
<CommandPrimitive.Group
ref={ref}
className={cn(
"overflow-hidden p-1 text-foreground [&[cmdk-group-heading]]:px-2 [&[cmdk-group-heading]]:py-1.5 [&[cmdk-group-heading]]:text-xs [&[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground",
className
)}
{...props}
/>
));

CommandGroup.displayName = CommandPrimitive.Group.displayName;

const CommandSeparator = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef

(({ className, ...props }, ref) => (
<CommandPrimitive.Separator
ref={ref}
className={cn("-mx-1 h-px bg-border", className)}
{...props}
/>
));
CommandSeparator.displayName = CommandPrimitive.Separator.displayName;

const CommandItem = React.forwardRef<
React.ElementRef,
React.ComponentPropsWithoutRef

(({ className, ...props }, ref) => (
<CommandPrimitive.Item
ref={ref}
className={cn(
"relative flex cursor-default select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none aria-selected:bg-accent aria-selected:text-accent-foreground data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
className
)}
{...props}
/>
));

CommandItem.displayName = CommandPrimitive.Item.displayName;

const CommandShortcut = ({
className,
...props
}: React.HTMLAttributes) => {
return (
<span
className={cn(
"ml-auto text-xs tracking-widest text-muted-foreground",
className
)}
{...props}
/>
);
};
CommandShortcut.displayName = "CommandShortcut";

export {
Command,
CommandDialog,
CommandInput,
CommandList,
CommandEmpty,
CommandGroup,
CommandItem,
CommandShortcut,
CommandSeparator,
};

Just wrap CommandGroup in a CommandList and it will work on latest cmdk. Move the CommandEmpty inside as well.

It should look like this: (just command, popover stuff removed for brevity)

      <Command>
        <CommandInput placeholder="Search ..." />
        <CommandList>
          <CommandEmpty>No option found.</CommandEmpty>
          <CommandGroup>
            {options.map((option) => {
              return (
                <CommandItem
                  key={option.value}
                  value={option.value}
                  onSelect={(currentValue) => {
                    setValue(currentValue === value ? "" : currentValue);
                    setOpen(false);
                    onValueChange(currentValue);
                  }}
                >
                  <CheckIcon
                    className={cn(
                      "mr-2 h-4 w-4",
                      value === option.value ? "opacity-100" : "opacity-0",
                    )}
                  />
                  {option.label}
                </CommandItem>
              );
            })}
          </CommandGroup>
        </CommandList>
      </Command>

Thanks man.