tailwindlabs / headlessui

Completely unstyled, fully accessible UI components, designed to integrate beautifully with Tailwind CSS.
https://headlessui.com
MIT License
26.28k stars 1.09k forks source link

React -Headless Popover 2.1V - close issue after router push #3524

Closed obulivignesh closed 1 month ago

obulivignesh commented 1 month ago

Edit from @RobinMalfait: added codeblocks for syntax highlighting


HI, Using Headless popover 2.1V. We have closing issue after input search clicking enter and redirecting through router.push(). Headless popover state was open after redirect to the page, we tried usestate, ref nothing helped. In DOM element still shows and state not updated.

But using buttonClose click it was working fine. we want onclick event to update the state = 'close'

Sample:

import { Popover, PopoverButton, PopoverGroup, PopoverPanel, CloseButton, Transition, TransitionChild, PopoverBackdrop, useClose } from "@headlessui/react";

export default function Header() {
    //   let close = useClose();
    const [search, searchpopup] = useState(true);
    const popoverRef = useRef(null);
    const handleSearch = () => {
        if (searchValue) {
            setSearchValue("");
            router.push(`/search/${searchValue.trim().split(" ").join("-")}`, {
                scroll: false,
            });

            if (popoverRef.current) {
                searchpopup(false);
                console.log(popoverRef.current);
                // close()
            }
        }
    };

    return (
        <>
            <PopoverGroup className="ml-8">
                <div className="flex h-full justify-center space-x-8">
                    <Popover className="flex">
                        <div className="relative flex ">
                            {/* custom-overlay on top */}
                            <PopoverButton
                                ref={popoverRef}
                                //  onClick={() => { close() }}
                                onClick={() => {
                                    searchpopup(true);
                                    handleSearch();
                                }}
                                className={`focus:outline-none p-2.5 rounded   ${showInput ? "active bg-black" : "bg-white"
                                    } `}
                            >
                               search
                            </PopoverButton>
                        </div>
                        <Transition
                            enter="transition ease-in-out duration-300 transform"
                            enterFrom="-translate-y-full opacity-0"
                            enterTo="translate-y-0 opacity-100"
                            leave="transition ease-in-out duration-300 transform"
                            leaveFrom="translate-y-0 opacity-100"
                            leaveTo="-translate-y-full opacity-100"
                        >
                            <PopoverPanel className="absolute  inset-x-0 top-full -z-10 custom-popover">
                                <>
                                    {search && (
                                        <>
                                            <PopoverBackdrop className="-z-50 fixed inset-0 h-screen  overlay-bg " />
                                            <div className="popup-background">
                                                <div className="bg-white">
                                                    <div className=" container mx-auto flex justify-between gap-4 py-5 mm_col bg-white relative px-8 lg:px-0">
                                                        <input
                                                            type="text"
                                                            placeholder="What are you looking for...?"
                                                            value={searchValue}
                                                            onChange={(event) =>
                                                                setSearchValue(event.target.value)
                                                            }
                                                            onKeyDown={(event) => {
                                                                if (event.key == "Enter") {
                                                                    handleSearch();
                                                                }
                                                            }}
                                                            className="filter-btn custom_input"
                                                        />
                                                        {searchValue && (
                                                            <button
                                                                className="absolute right-28 top-7 text-gray-500"
                                                                onClick={() => setSearchValue("")}
                                                            >
                                                                <svg
                                                                    width="24"
                                                                    height="24"
                                                                    viewBox="0 0 24 24"
                                                                    fill="none"
                                                                    xmlns="http://www.w3.org/2000/svg"
                                                                >
                                                                    <path
                                                                        d="M18 6L6 18M6 6L18 18"
                                                                        stroke="black"
                                                                        strokeWidth="2"
                                                                        strokeLinecap="round"
                                                                        strokeLinejoin="round"
                                                                    />
                                                                </svg>
                                                            </button>
                                                        )}
                                                        <CloseButton
                                                            className={`${searchValue ? " " : "opacity-20"
                                                                } bg-white text-gray-800 font-semibold py-2 px-4 border border-gray-400 rounded shadow`}
                                                            onClick={handleSearch}
                                                            disabled={!searchValue}
                                                        >
                                                            Search
                                                        </CloseButton>
                                                    </div>
                                                </div>
                                            </div>
                                        </>
                                    )}
                                </>
                            </PopoverPanel>
                        </Transition>
                    </Popover>
                </div>
            </PopoverGroup>
        </>
    );
}

Thanks in advance.

Dharmil4602 commented 1 month ago

I can work on this issue. Please assign it to me. Thanks

obulivignesh commented 1 month ago

I can work on this issue. Please assign it to me. Thanks @Dharmil4602

Updated it

RobinMalfait commented 1 month ago

Hey! The codesnippet has a lot of missing parts and it's hard to replicate the problem with just that. Can you generate a minimal GitHub repo or CodeSandbox where the issue is clear?

You don't need the ref, or onClick or the state for controlling the state of the Popover. The CloseButton when submitting the form should be enough already. Can you try to use that on its own without anything else?

You also can't use the useClose() hook because it needs to be inside of the Popover itself, but right now it's in the Header component which means that it doesn't know about the Popover component. (under the hood, the CloseButton uses that hook so it should work already)

While waiting for a reproduction, a few things you can try are:

  1. Use the <CloseButton /> without anything else
  2. Make sure you are on the latest version of Headless UI
  3. Make sure to remove the .next folder and restart your dev server

Locally, when I add some of the missing pieces, the Popover closes as expected when clicking on the Search button:

import {
  CloseButton,
  Popover,
  PopoverBackdrop,
  PopoverButton,
  PopoverGroup,
  PopoverPanel,
  Transition,
} from '@headlessui/react'
import { useState } from 'react'

export default function Header() {
  const showInput = true
  const [searchValue, setSearchValue] = useState('')
  const handleSearch = () => {
    console.log('Handling search')
  }

  return (
    <>
      <PopoverGroup className="ml-8">
        <div className="flex h-full justify-center space-x-8">
          <Popover className="flex">
            <div className="relative flex ">
              {/* custom-overlay on top */}
              <PopoverButton
                className={`rounded p-2.5 focus:outline-none   ${
                  showInput ? 'active bg-black' : 'bg-white'
                }`}
              >
                search
              </PopoverButton>
            </div>
            <Transition
              enter="transition ease-in-out duration-300 transform"
              enterFrom="-translate-y-full opacity-0"
              enterTo="translate-y-0 opacity-100"
              leave="transition ease-in-out duration-300 transform"
              leaveFrom="translate-y-0 opacity-100"
              leaveTo="-translate-y-full opacity-100"
            >
              <PopoverPanel className="custom-popover  absolute inset-x-0 top-full -z-10">
                <PopoverBackdrop className="overlay-bg fixed inset-0 -z-50  h-screen " />
                <div className="popup-background">
                  <div className="bg-white">
                    <div className=" mm_col container relative mx-auto flex justify-between gap-4 bg-white px-8 py-5 lg:px-0">
                      <input
                        type="text"
                        placeholder="What are you looking for...?"
                        value={searchValue}
                        onChange={(event) => setSearchValue(event.target.value)}
                        onKeyDown={(event) => {
                          if (event.key == 'Enter') {
                            handleSearch()
                          }
                        }}
                        className="filter-btn custom_input"
                      />
                      {searchValue && (
                        <button
                          className="absolute right-28 top-7 text-gray-500"
                          onClick={() => setSearchValue('')}
                        >
                          <svg
                            width="24"
                            height="24"
                            viewBox="0 0 24 24"
                            fill="none"
                            xmlns="http://www.w3.org/2000/svg"
                          >
                            <path
                              d="M18 6L6 18M6 6L18 18"
                              stroke="black"
                              strokeWidth="2"
                              strokeLinecap="round"
                              strokeLinejoin="round"
                            />
                          </svg>
                        </button>
                      )}
                      <CloseButton
                        className={`${
                          searchValue ? ' ' : 'opacity-20'
                        } rounded border border-gray-400 bg-white px-4 py-2 font-semibold text-gray-800 shadow`}
                        onClick={handleSearch}
                        disabled={!searchValue}
                      >
                        Search
                      </CloseButton>
                    </div>
                  </div>
                </div>
              </PopoverPanel>
            </Transition>
          </Popover>
        </div>
      </PopoverGroup>
    </>
  )
}

Now that said, this only works when you click on the button or press enter while the button is focused. If you press Enter in the search field it will not close the Popover. The reason for this is that the input and the button aren't connected.

To solve this, make sure to wrap them in a <form> so that they are connected. That way you can also simplify the code by handing the onSubmit.

Hope this helps!