ycs77/headlessui-float

Specifying the show prop for a popover the default functionality is disabled

aaronabf opened this issue · 1 comments

Use Version
Use version when bugs appear:

  • Headless UI: v1.7.14
  • Headless UI Float: v0.11.2
  • Framework: React v18.2.0
  • Browser: Chrome

Describe the bug

In a Popover, when the prop show is specified, the default behavior is disabled. In particular, the Popover's button does not work and useOutsideClick also is not enabled. It is a common use case to want to provide additional showing functionality on top of the existing functionality. For example, we have 3 instances in our codebase where an outside event should close the popover.

To Reproduce

import React, { useState } from "react";
import { Popover } from "@headlessui/react";
import { Float } from "@headlessui-float/react";

export default function Example() {
  const [open, setOpen] = useState(false);

  const toggleMenu = () => setOpen(!open);

  return (
    <div className="flex flex-col gap-2">
      <button className="p-3 border w-[400px]" onClick={toggleMenu}>
        not popover button
      </button>
      <Popover>
        <Float show={open}>
          <Popover.Button className="p-3 border w-[400px]">
            popover button
          </Popover.Button>
          <Popover.Panel className="p-3 border w-[400px]">
            popover panel
          </Popover.Panel>
        </Float>
      </Popover>
    </div>
  );
}
ycs77 commented

Hi @aaronabf, when enabling manual control of the floating element display, it is normal that useOutsideClick() will not work, because at this time the floating element should be completely static, and all display or not is completely controlled by the user.

If you want to using useOutsideClick(), you can refer to the following example. First, you need to import useOutsideClick on @headlessui-float/react, and then bind the Ref of the button (buttonRef) and the floating element (floatingRef). However, the Ref of the floating element cannot be bound to <Popover.Panel>, and you can add a <div> inside. <Popover.Panel> needs to add static to set it as a static element.

import React, { useRef, useState } from 'react'
import { Popover } from '@headlessui/react'
// import `useOutsideClick()` on '@headlessui-float/react'
import { Float, useOutsideClick } from '@headlessui-float/react'

export default function Example() {
  const buttonRef = useRef(null)
  const floatingRef = useRef(null)
  const [open, setOpen] = useState(false)

  const toggleMenu = () => setOpen(!open)

  // on click outside of floating element to close
  useOutsideClick([buttonRef, floatingRef], () => {
    setOpen(false)
    buttonRef.current.focus()
  }, open)

  return (
    <div className="flex flex-col gap-2">
      <button ref={buttonRef} className="p-3 border w-[400px] focus:ring" onClick={toggleMenu}>
        not popover button
      </button>
      <Popover>
        <Float show={open}>
          <Popover.Button className="p-3 border w-[400px] focus:ring">
            popover button
          </Popover.Button>
          {/* add `static` */}
          <Popover.Panel static>
            {/* must set ref={floatingRef} into inner, don't set to `<Popover.Panel>` */}
            <div ref={floatingRef} className="p-3 border w-[400px]">
              popover panel
              <input type="text" placeholder="typeing..." className="ml-2" />
            </div>
          </Popover.Panel>
        </Float>
      </Popover>
    </div>
  )
}