Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bug on headlessui Popover #1625

Closed
M4RC0Sx opened this issue Sep 1, 2024 · 12 comments · Fixed by tailwindlabs/headlessui#3448 or tailwindlabs/headlessui#3452
Closed

Bug on headlessui Popover #1625

M4RC0Sx opened this issue Sep 1, 2024 · 12 comments · Fixed by tailwindlabs/headlessui#3448 or tailwindlabs/headlessui#3452
Assignees

Comments

@M4RC0Sx
Copy link

M4RC0Sx commented Sep 1, 2024

What component (if applicable)

Describe the bug
Using the exact component you have on the web on a NextJS 14.2.7 with React 18 app, if you spam click the toggle mobile navbar button which open and closes de Popover, it stops working. If I do it on your web it is not happening. Using @headlessui/react@2.1.2 becasue 2.1.3 totally breaks the popover open-close behavior.

To Reproduce

  1. Make a small screen resolutoon to simulate a mobile phone and make the navbar collapse.
    1. Spam click the toggle popover menu button.

Expected behavior
You can spam click the button and the popover menu just opens and closes normally.

Screenshots
Not applicable

Browser/Device (if applicable)

  • OS: [e.g. iOS] Windows 11
  • Browser [e.g. chrome, safari] Firefox
  • Version [e.g. 22] 129.0.2
@reinink reinink self-assigned this Sep 2, 2024
@reinink
Copy link
Member

reinink commented Sep 2, 2024

Hey @M4RC0Sx, thanks for bringing this to our attention. I'm going to close this issue in favor of tailwindlabs/headlessui#3438 (I see you commented there too). Hopefully we can get this one fixed up quickly, but until then I recommend staying on v2.1.2 👍

@reinink reinink closed this as completed Sep 2, 2024
RobinMalfait added a commit to tailwindlabs/headlessui that referenced this issue Sep 3, 2024
…3448)

This PR fixes a bug where the components don't always properly close
when using the `transition` prop on those components.

The issue here is that the internal `useTransition(…)` hook relies on a
DOM node. Whenever the DOM node changes, we need to re-run the
`useTransition(…)`. This is why we store the DOM element in state
instead of relying on a `useRef(…)`.

Let's say you have a `Popover` component, then the structure looks like
this:
```ts
<Popover>
  <PopoverButton>Show</PopoverButton>
  <PopoverPanel>Contents</PopoverPanel>
</Popover>
```

We store a DOM reference to the button and the panel in state, and the
state lives in the `Popover` component. The reason we do that is so that
the button can reference the panel and the panel can reference the
button. This is needed for some `aria-*` attributes for example:
```ts
<PopoverButton aria-controls={panelElement.id}>
```

For the transitions, we set some state to make sure that the panel is
visible or hidden, then we wait for transitions to finish by listening
to transition related events on the DOM node directly.

If you now say, "hey panel, please re-render because you have to become
visible/hidden" then the component re-renders, the panel DOM node
(stored in the `Popover` component) eventually updates and then the
`useTransition(…)` hooks receives the new value (either the DOM node or
null when the leave transition is complete).

The problem here is the round trip that it first has to go to the root
`<Popover/>` component, re-render everything and provide the new DOM
node to the `useTransition(…)` hook.

The solution? Local state so that the panel can re-render on its own and
doesn't require the round trip via the parent.

Fixes: #3438
Fixes: #3437
Fixes: tailwindlabs/tailwindui-issues#1625

---------

Co-authored-by: Jonathan Reinink <jonathan@reinink.ca>
@RobinMalfait
Copy link
Member

RobinMalfait commented Sep 3, 2024

Hey sorry about the regression!

This should be fixed, and is available in the next release of Headless UI (2.1.4).

@M4RC0Sx
Copy link
Author

M4RC0Sx commented Sep 3, 2024

Hey sorry about the regression!

This should be fixed, and is available in the next release of Headless UI (2.1.4).

Hi @RobinMalfait ! This is not fixed in 2.1.4, when you spam-click the button it stops working. Could you reopen the issue?

@RobinMalfait
Copy link
Member

Hmm, can you verify a few things for me?

  1. Verify that you are on the latest version npm ls @headlessui/react should give you the info. But you can also remove node_modules, npm install and then npm install @headlessui/react@latest to be sure.
  2. Make sure you don't have any browser or node related caches going on. For example, if you are using Next.js, make sure to remove the .next folder.
  3. Make sure you restart your dev server in case there are some in-memory caches that aren't flushed correctly. (This can happen in Next.js but I've also seen it happen in Vite related projects).

That said, this is the behavior I see:

  1. On 2.1.3, spamming the PopoverButton eventually (pretty quickly) breaks the Popover
  2. On 2.1.4 spamming the PopoverButton doesn't break the Popover entirely. Do note that it's not an instant open / close due to the transition that is happening. But eventually when I stop spamming the button, it behaves as expected.

Caution

Due to the spamming of the button, there will be flashing content in the video below. This video may potentially trigger seizures for people with photosensitive epilepsy.

Screen.Recording.2024-09-04.at.10.32.44.mov

@M4RC0Sx
Copy link
Author

M4RC0Sx commented Sep 4, 2024

Hi @RobinMalfait . Just tried it with all the steps you said and still getting same issue.

@RobinMalfait
Copy link
Member

@M4RC0Sx can you share a (minimal) reproduction GitHub repo by any chance so that I can take a look?

@M4RC0Sx
Copy link
Author

M4RC0Sx commented Sep 4, 2024

Just invited you to my repo. Latest changes with your steps are on develop branch. @RobinMalfait

@RobinMalfait
Copy link
Member

RobinMalfait commented Sep 4, 2024

I see the same correct behavior on your repo:

Screen.Recording.2024-09-04.at.10.59.35.mov

Assuming you did all the steps I mentioned:

  1. stop npm run dev script
  2. rm -rf node_modules
  3. rm -rf .next
  4. npm install
  5. npm install @headlessui/react@lateset
  6. npm run dev

Can you share which OS you are using, which browser and what version of that browser?

@M4RC0Sx
Copy link
Author

M4RC0Sx commented Sep 4, 2024

Yes, all the steps done @RobinMalfait . I am using Windows 11 PRO 23H2 with WSL Ubuntu 20.04.6.
I am on Firefox 130.0 64 bits.

RobinMalfait added a commit to tailwindlabs/headlessui that referenced this issue Sep 4, 2024
…on` in rapid succession (#3452)

We recently landed a fix for `Popover`s not closing correctly when using
the `transition` prop (#3448). Once this fix was published, some users
still ran into issues using Firefox on Windows (see:
tailwindlabs/tailwindui-issues#1625).

One fun thing I discovered is that transitions somehow behave
differently based on where they are triggered from (?). What I mean by
this is that holding down the <kbd>space</kbd> key on the button does
properly open/close the `Popover`. But if you rapidly click the button,
the `Popover` will eventually get stuck.

> Note: when testing this, I made sure that the handling of the `space`
key (in a `keydown` handler) and the clicking of the mouse (handled in a
`click` handler) called the exact same code. It still happened.

The debugging continues…

One thing I noticed is that when the `Popover` gets stuck, it meant that
a transition didn't properly complete.

The current implementation of the internal `useTransition(…)` hook has
to wait for all the transitions to finish. This is done using a
`waitForTransition(…)` helper. This helper sets up some event listeners
(`transitionstart`, `transitionend`, …) and waits for them to fire.

This seems to be unreliable on Firefox for some unknown reason.

I knew the code for waiting for transitions wasn't ideal, so I wanted to
see if using the native `node.getAnimations()` simplifies this and makes
it work in general.

Lo and behold, it did! 🎉

This now has multiple benefits:

1. It works as expected on Firefox
2. The code is much much simpler
3. Uses native features

The `getAnimations(…)` function is supported in all modern browsers
(since 2020). At the time it was too early to rely on it, but right now
it should be safe to use.

Fixes: tailwindlabs/tailwindui-issues#1625
@RobinMalfait
Copy link
Member

Hey @M4RC0Sx

I was able to reproduce this issue on Firefox. I wrote more about it here: #3452

Before publishing a new version, could you try with this insiders version:

npm install @headlessui/react@0.0.0-insiders.971ff6b

Let me know if this behaves the way you expect it to behave 👍

@M4RC0Sx
Copy link
Author

M4RC0Sx commented Sep 4, 2024

Hi @RobinMalfait ! Thank you very much! Works like a charm with the insiders version!

@RobinMalfait
Copy link
Member

Beautiful! I published this as a new version 2.1.5

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment