ui icon indicating copy to clipboard operation
ui copied to clipboard

`Popover` don't open when combined with `Tooltip`

Open piszczu4 opened this issue 7 months ago • 5 comments

I tried to make TooltipButton and PopoverButton components to nicely nest them when needed. However, Popover won't open when doing it (and vice versa). Here is my demo: https://codesandbox.io/p/sandbox/tooltip-popover-forked-dh3zq3?file=%2Fsrc%2FApp.tsx%3A58%2C40

While tooltip works fine, popover won't open. If I nest popover inside tooltip, then popover works but tooltip doesnt. What should I change? I would really like to have separate components for Tooltip and Popover instead of having to nest them in single component like describe here: https://www.radix-ui.com/primitives/docs/guides/composition#composing-multiple-primitives

piszczu4 avatar Jan 24 '24 19:01 piszczu4

I could be wrong but shouldn't your code look like this?

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export function PopoverDemo() {
  return (
    <TooltipProvider>
      <Popover>
        <PopoverTrigger asChild>
          <Tooltip>
            <TooltipTrigger asChild>
              <Button variant="outline">Open popover</Button>
            </TooltipTrigger>
            <TooltipContent>
              <p>Add to library</p>
            </TooltipContent>
          </Tooltip>
        </PopoverTrigger>
        <PopoverContent className="w-80">
          <div className="grid gap-4">
            <div className="space-y-2">
              <h4 className="font-medium leading-none">Dimensions</h4>
              <p className="text-sm text-muted-foreground">
                Set the dimensions for the layer.
              </p>
            </div>
            <div className="grid gap-2">
              <div className="grid grid-cols-3 items-center gap-4">
                <Label htmlFor="width">Width</Label>
                <Input
                  id="width"
                  defaultValue="100%"
                  className="col-span-2 h-8"
                />
              </div>
              <div className="grid grid-cols-3 items-center gap-4">
                <Label htmlFor="maxWidth">Max. width</Label>
                <Input
                  id="maxWidth"
                  defaultValue="300px"
                  className="col-span-2 h-8"
                />
              </div>
              <div className="grid grid-cols-3 items-center gap-4">
                <Label htmlFor="height">Height</Label>
                <Input
                  id="height"
                  defaultValue="25px"
                  className="col-span-2 h-8"
                />
              </div>
              <div className="grid grid-cols-3 items-center gap-4">
                <Label htmlFor="maxHeight">Max. height</Label>
                <Input
                  id="maxHeight"
                  defaultValue="none"
                  className="col-span-2 h-8"
                />
              </div>
            </div>
          </div>
        </PopoverContent>
      </Popover>
    </TooltipProvider>
  );
}

I'm sorry if I didn't understand what you meant.

Caio-Tera avatar Jan 24 '24 20:01 Caio-Tera

@Caio-Tera I'd like to create to separate components, one for TooltipButton and another one for PopoverButton and eventually use them both when needed instead of crating this long chain as you did. Why my code doesnt work, i.e. this part?:

  const tooltipButtonWithPopover = (
    <TooltipButton trigger={popoverButtonWithoutTooltip}>
      Tooltip Content
    </TooltipButton>
  );

piszczu4 avatar Jan 24 '24 21:01 piszczu4

Does this make any sense to you?

I couldn't test whether it renders.

But I imagine this is the beginning of a solution?

import React from 'react';

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';

export function PopoverComponent({
  trigger,
  body,
}: {
  trigger: React.ReactNode;
  body: React.ReactNode;
}) {
  return (
    <Popover>
      <PopoverTrigger asChild>{trigger}</PopoverTrigger>
      <PopoverContent className="w-80">{body}</PopoverContent>
    </Popover>
  );
}

export function TooltipComponent({
  trigger,
  body,
}: {
  trigger: React.ReactNode;
  body: React.ReactNode;
}) {
  return (
    <Tooltip>
      <TooltipTrigger asChild>{trigger}</TooltipTrigger>
      <TooltipContent>{body}</TooltipContent>
    </Tooltip>
  );
}

export function PopoverBody() {
  return (
    <div className="grid gap-4">
      <div className="space-y-2">
        <h4 className="font-medium leading-none">Dimensions</h4>
        <p className="text-sm text-muted-foreground">
          Set the dimensions for the layer.
        </p>
      </div>
      <div className="grid gap-2">
        <div className="grid grid-cols-3 items-center gap-4">
          <Label htmlFor="width">Width</Label>
          <Input id="width" defaultValue="100%" className="col-span-2 h-8" />
        </div>
        <div className="grid grid-cols-3 items-center gap-4">
          <Label htmlFor="maxWidth">Max. width</Label>
          <Input
            id="maxWidth"
            defaultValue="300px"
            className="col-span-2 h-8"
          />
        </div>
        <div className="grid grid-cols-3 items-center gap-4">
          <Label htmlFor="height">Height</Label>
          <Input id="height" defaultValue="25px" className="col-span-2 h-8" />
        </div>
        <div className="grid grid-cols-3 items-center gap-4">
          <Label htmlFor="maxHeight">Max. height</Label>
          <Input
            id="maxHeight"
            defaultValue="none"
            className="col-span-2 h-8"
          />
        </div>
      </div>
    </div>
  );
}

export function Rootlayout() {
  return (
    <TooltipProvider>
      <PopoverComponent
        trigger={
          <TooltipComponent
            trigger={<Button variant="outline">Open popover</Button>}
            body={<p>Add to library</p>}
          />
        }
        body={<PopoverBody />}
      />
    </TooltipProvider>
  );
}

Caio-Tera avatar Jan 26 '24 12:01 Caio-Tera

@Caio-Tera how does it differ from my current implementaion? I also splitted it into to separate components which does not work. I would appreciate if someone could actually fix my code and indicate what was wrong with him.

piszczu4 avatar Jan 26 '24 15:01 piszczu4

This worked for me - Tooltip and Popover working together:

import {
  Tooltip,
  TooltipContent,
  TooltipTrigger,
} from "@/components/ui/tooltip";

type ToolbarTooltipProps = {
  content: string; // Or could be another React.ReactNode.
  children: React.ReactNode;
};

export const ToolbarTooltip = ({ content, children }: ToolbarTooltipProps) => {
  return (
    <Tooltip>
      <TooltipTrigger asChild>{children}</TooltipTrigger>
      <TooltipContent side="bottom" sideOffset={20}>
        {content}
      </TooltipContent>
    </Tooltip>
  );
};
"use client";

import { Button } from "@/components/ui/button";
import {
  Popover,
  PopoverContent,
  PopoverTrigger,
} from "@/components/ui/popover";
import { ToolbarTooltip } from "./ToolbarTooltip";

export const SomeComponent = () => {
  return (
    <Popover>
      <ToolbarTooltip content="Background">
        <PopoverTrigger asChild>
          <Button variant={"ghost"} className="h-full">
            Trigger
          </Button>
        </PopoverTrigger>
      </ToolbarTooltip>
      <PopoverContent className="z-50">....</PopoverContent>
    </Popover>
  );
};

jrandeniya avatar Feb 05 '24 21:02 jrandeniya

This issue has been automatically closed because it received no activity for a while. If you think it was closed by accident, please leave a comment. Thank you.

shadcn avatar Feb 28 '24 23:02 shadcn

in my case this is worked for me

<TooltipProvider>
        <Popover>
          <Tooltip delayDuration={100}>
            <PopoverTrigger asChild>
              <TooltipTrigger asChild>
                <Button size={"icon"} variant={"ghost"}>
                  {icon}
                </Button>
              </TooltipTrigger>
            </PopoverTrigger>
            <TooltipContent
              side="right"
              className="bg-secondary font-semibold text-foreground"
            >
              <p>{title}</p>
              <TooltipArrow className="fill-secondary" />
            </TooltipContent>
          </Tooltip>
          <PopoverContent side="right">
            <div>{children}</div>
            <PopoverArrow className="fill-popover" />
          </PopoverContent>
        </Popover>
      </TooltipProvider>

Sisableng avatar Jun 03 '24 10:06 Sisableng