ui icon indicating copy to clipboard operation
ui copied to clipboard

[bug]: AccordionTrigger throws error with asChild

Open UltimateGG opened this issue 1 year ago • 2 comments

Describe the bug

If you try to use the asChild prop with AccordionTrigger React throws an error because we try to render a icon and our children, but it can only render 1 (Radix limitation)

Affected component/components

Accordion

How to reproduce

Add asChild prop to an AccordionTrigger

Codesandbox/StackBlitz link

No response

Logs

No response

System Info

None

Before submitting

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

UltimateGG avatar Sep 03 '24 18:09 UltimateGG

Heres my fix:

export const AccordionTrigger = React.forwardRef<React.ElementRef<typeof AccordionPrimitive.Trigger>, React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>>(
  ({ className, children, ...props }, ref) => {
    const content = props.asChild ? (
      children
    ) : (
      <>
        {children}
        <ChevronDown className="size-4 shrink-0 transition-transform duration-200" />
      </>
    );

    return (
      <AccordionPrimitive.Header className="flex">
        <AccordionPrimitive.Trigger
          ref={ref}
          className={cn('flex flex-1 text-[16px] tracking-normal items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180', className)}
          {...props}
        >
          {content}
        </AccordionPrimitive.Trigger>
      </AccordionPrimitive.Header>
    );
  }
);

UltimateGG avatar Sep 03 '24 18:09 UltimateGG

I agree with this


I understand “asChild” in “radix-ui” to be a way to replace the default rendering element with the one you specify.

For example <AccordionTrigger> is <button> by default, but <AccordionTrigger asChild><div>test</div></AccordionTrigger> should be <div> by default

Default EX:

  <AccordionTrigger>
    <div>test</div>
  </AccordionTrigger>
스크린샷 2024-09-07 오전 8 13 21

asChild EX:

  <AccordionTrigger asChild>
    <div>test</div>
  </AccordionTrigger>
스크린샷 2024-09-07 오전 8 13 44

But “accrodion” in “shadcn” contains an icon by default So the “children” in <AccordionPrimitive.Header> renders two elements, returning an error

shadcn Default EX:

  <AccordionPrimitive.Header className="flex">
    <AccordionPrimitive.Trigger
      ref={ref}
      className={cn(
        "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
        className
      )}
      {...props}
    >
      <div>test</div>
      <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
    </AccordionPrimitive.Trigger>
  </AccordionPrimitive.Header>

shadcn asChild EX:

  <AccordionPrimitive.Header className="flex">
    <div
      ref={ref}
      className={cn(
        "flex flex-1 items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
        className
      )}
      {...props}
    >
      test
    </div>
    <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
  </AccordionPrimitive.Header>

Two React elements are being rendered in “<AccordionPrimitive.Header>” as shown in the code example above

However, “radix-ui” receives a child component as “children” in <AccordionPrimitive.Header>, which violates the React rule “children can only receive one child component”

@shadcn

joseph0926 avatar Sep 06 '24 23:09 joseph0926

I have this issue too.

It's critical for SEO apps.

currently there's no way to set Head aschild

lyogavin avatar Jan 13 '25 15:01 lyogavin

I have to do this:


const AccordionTrigger = React.forwardRef<
  React.ElementRef<typeof AccordionPrimitive.Trigger>,
  React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
>(({ className, children, ...props }, ref) => {
  const content = props.asChild ? (
    <AccordionPrimitive.Header className="flex" asChild>
      <AccordionPrimitive.Trigger
        ref={ref}
        className={cn(
          "flex flex-1 text-[16px] tracking-normal items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
          className
        )}
        {...props}
      >
        <div className="flex items-center gap-2">
          {children}
          <ChevronDown className="size-4 shrink-0 transition-transform duration-200" />
        </div>
      </AccordionPrimitive.Trigger>
    </AccordionPrimitive.Header>
  ) : (
    (
      <AccordionPrimitive.Header className="flex">
        <AccordionPrimitive.Trigger
          ref={ref}
          className={cn(
            "flex flex-1 text-[16px] tracking-normal items-center justify-between py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
            className
          )}
          {...props}
        >
          {children}
          <ChevronDown className="size-4 shrink-0 transition-transform duration-200" />
        </AccordionPrimitive.Trigger>
      </AccordionPrimitive.Header>
    )
  );

lyogavin avatar Jan 13 '25 16:01 lyogavin

What about this bug?

kamami avatar Jul 15 '25 13:07 kamami