tailwindlabs / headlessui

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

A menu with a transition as a fragment with <Menu.Items> child both with unmount=false does not respond to ESC key first time it's opened #3497

Open TomestoneGG opened 2 days ago

TomestoneGG commented 2 days ago

A menu with a transition as a fragment with child both with unmount=false does not respond to ESC key first time it's opened. The second and subsequent times you open it, it works.

Small example component that has the issue:

import { Fragment } from 'react'
import { Menu, Transition } from '@headlessui/react'
import { ChevronDownIcon } from '@heroicons/react/20/solid'
import InternalLink from "@/Components/InternalLink.jsx";

function classNames(...classes) {
    return classes.filter(Boolean).join(' ')
}

export default function NavigationDropdown({label, className, labelClassName, itemGroups, only, preserveScroll, preserveState, preserveQuery, hidden, labelHeader}) {
    function queryParams(...args) {

        let queryString = window.location.search

        if (!queryString || queryString === '?') {
            return '';
        }

        let searchParams = new URLSearchParams(queryString)
        searchParams.delete('page')

        return '?' + searchParams.toString()
    }

    return (
        <Menu as="div" className={(hidden ? 'hidden ' : '') + ` relative inline-block flex-1 text-left text-gray-300 md:hover:text-white ${className}`}>
            <div>
                <Menu.Button className={`inline-flex w-full items-center px-4 py-2 shadow-sm focus:outline-none focus:ring-0 ${labelClassName}`}>
                    {!labelHeader && <div className={"text-left"}>{label}</div>}
                    {labelHeader && <h1 className={"text-left"}>{label}</h1>}
                    <div className={"w-5 h-5"}>
                        <ChevronDownIcon className="-mr-1 ml-2 w-5 h-5" aria-hidden="true" />
                    </div>
                </Menu.Button>
            </div>

            <Transition
                as={Fragment}
                unmount={false}
                enter="transition ease-out duration-100"
                enterFrom="transform opacity-0 scale-95"
                enterTo="transform opacity-100 scale-100"
                leave="transition ease-in duration-75"
                leaveFrom="transform opacity-100 scale-100"
                leaveTo="transform opacity-0 scale-95"
            >
                <Menu.Items unmount={false} className="sm:absolute bg-black left-0 right-0 md:right-auto z-10 mt-1 p-1 min-w-[20rem] origin-top-right divide-y divide-gray-800
                            font-medium text-sm border-solid border border-gray-800 rounded-md shadow-lg ring-1 ring-white ring-opacity-5 focus:outline-none">
                    {itemGroups.map((itemGroup, index) => (
                        <div key={index}>
                            {itemGroup.map((item, index) => (
                                <Menu.Item key={index}>
                                    {({ active }) => (
                                        <InternalLink key={index}
                                              href={item.href + (preserveQuery ? queryParams() : '')}
                                              only={only}
                                              preserveScroll={preserveScroll}
                                              preserveState={preserveState}
                                              className={classNames(
                                                active ? 'bg-blue-700 text-white' : 'text-gray-300',
                                                'block rounded rounded-md px-4 py-2  '
                                            )}
                                        >
                                            {item.label}
                                        </InternalLink>
                                    )}
                                </Menu.Item>
                                ))}
                        </div>))}
                </Menu.Items>
            </Transition>
        </Menu>
    )
}

It is the presence of the unmount=false that cause this issue to occur. It works fine otherwise. I need unmount=false to be set in order for SEO to work well with the site.