shadcn-ui / ui

Beautifully designed components that you can copy and paste into your apps. Accessible. Customizable. Open Source.
https://ui.shadcn.com
MIT License
62.69k stars 3.52k forks source link

[bug]: Combo box is not working. #3707

Open akeakun opened 1 month ago

akeakun commented 1 month ago

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

QAQmmttyyy commented 1 month ago

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

QAQmmttyyy commented 1 month ago

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

0-don commented 1 month ago

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>
  );
};
Woofer21 commented 1 month ago

you need to use this version, v1 doesnt work

    "cmdk": "^0.2.1",

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

HiggsWRX commented 1 month ago

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>
johncarmack1984 commented 1 month ago

you need to use this version, v1 doesnt work

    "cmdk": "^0.2.1",

Weekend saved, nice catch

DamifeZion commented 1 month ago

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}
  </Dialog>

); };

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, };

Olalexy1 commented 1 month ago

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.