Commit Graph

15 Commits

Author SHA1 Message Date
Jordan Pittman 0ff2326171 Don’t fire afterLeave event more than once for a given transition (#2267)
* Don’t fire afterLeave event more than once for a given component

* Port test to React

* Fix CS

* Remove focus on test

* Update changelog
2023-02-10 09:36:09 -05:00
Robin Malfait 4da0b3aba9 Improve syncing of the Combobox.Input value (#2042)
* make combobox playgrounds in React and Vue similar

* syncing of the input should happen when the value changes internally or externally

I also got rid of the manually dispatching of the change event if the
value changes from internally.

I think the correct mental model is:
- That the `Combobox.Input` value should change if the selected value
  changes from the outside or from the inside.
  - Note: It should _not_ do that if you are currently typing (once you
    choose a new value it will re-sync, once you reset (escape / outside
    click) it will also sync again).
- The `onChange`/`onInput` of the `Combobox.Input` itself should only be
  called if you as the user type something. Not when the value is
  "synced" based on the selected value. We were currently manually
  dispatching events which works (to a certain extend) but smelled a bit
  fishy to me.

The manual dispatching of events tried to solve an issue
(https://github.com/tailwindlabs/headlessui/issues/1875), but I think
this can be solved in 2 other ways that make a bit more sense:

1. (Today) Use the `onBlur` on the input to reset the `query` value to filter
   options.
2. (In the future)  Use an exposed `onClose` (or similar) event to reset
   your `query` value.

* update changelog

* ignore flakey test
2022-11-23 16:16:10 +01:00
Robin Malfait 3db54db1ba Fix ref stealing from children (#1820)
* fix ref stealing

When a higher-level component (like `Transition`) provides a `ref` to
its child component, then it will override the `ref` that was
potentially already on the child.

This will make sure that these are merged together correctly.

Fixes: #985

* update changelog
2022-09-05 13:09:23 +02:00
Robin Malfait a98e55c34c Fix Transition component's incorrect cleanup and order of events (#1803)
* improve tracking of transitionableChildren

* remove weird outlier snapshots

If anything is still wrong the tests will still fail but the diffs will
be easier to read.

* remove event handling from `useTransition`

* handle before/after events in `Transition` directly

* fix incorrect logic bug in tests

* add very explicit test for transition event order

* ignore flakey tests for now

We will get back to these!

* ensure cancellation of transitions works properly

* update changelog
2022-09-02 00:44:52 +02:00
Robin Malfait b71fcb3b46 temporary ignore flaky tests 2022-05-02 20:52:02 +02:00
Robin Malfait cb7a969951 General cleanup of README files (#1361)
* cleanup README files

* ignore flakey tests for now

There are a handful of tests that are pretty flakey and fail every once
in a while on CI, I don't want to remove them yet, but rather ignore
them for now.

I am going to experiment with using Playwright/Puppeteer to use a real
browser instead.
2022-04-24 01:05:17 +02:00
Robin Malfait 0162c57d88 add React 18 compatibility (#1326)
* bump dev dependencies to React 18

* setup Jest to include `IS_REACT_ACT_ENVIRONMENT`

* prefer `useId` from React 18 if it exists

In React 16 & 17, where `useId` doesn't exist, we will fallback to our
implementation we have been using up until now.

The `useId` exposed by React 18, ensures stable references even in SSR
environments.

* update expected events

React 18 now uses the proper events:
- `blur` -> `focusout`
- `focus` -> `focusin`

* ensure to wait a bit longer

This is a bit unfortunate, but since React 18 now does an extra
unmount/remount in `StrictMode` to ensure that your code is
ConcurrentMode ready, it takes a bit longer to settle what the DOM sees.

That said, this is a temporary "hack". We are going to experiment with
using tools like Puppeteer/Playwright to run our tests in an actual
browser instead to eliminate all the weird details that we have to keep
in mind.

* prefer `.focus()` over `fireEvent.focus(el)`

* abstract `microTask` polyfill code

* prefer our `focus(el)` function over `el.focus()`

Internally we would still use `el.focus()`, but this allows us to have
more control over that `focus` function.

* add React 18 to the React Playground

* improve hooks for React 18

- Improving the cleanup of useEffect hooks
- useIsoMorphicEffect instead of normal useEffect, so that we can use
  useLayoutEffect to be a bit quicker.

* improve disposables

- This allows us to add event listeners on a node, and get automatic
  cleanup once `dispose` gets called.
- We also return all the `d.add` calls, so that we can cleanup specific
  parts only instead of everything or nothing.

* reimplement the Transition component to be React 18 ready

* wait an additional frame for everything to settle

* update playground examples

* suppressConsoleLogs for RadioGroup components

* update changelog

* keep the `to` classes for a smoother transition

In the next transition we will remove _all_ classes provided and re-add
the once we need.

---

Some extra special thanks:

- Thanks @silvenon for your initial work on the `transition` events in #926
- Thanks @thecrypticace for doing late-night debugging sessions

Co-authored-by: =?UTF-8?q?Matija=20Marohni=C4=87?= <matija.marohnic@gmail.com>
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2022-04-13 22:07:01 +02:00
Robin Malfait fdd2629795 Improve overal codebase, use modern tech like esbuild and TypeScript 4! (#1055)
* use esbuild for React instead of tsdx

* remove tsdx from Vue

* use consistent names

* add jest and prettier

* update scripts

* ignore some folders for prettier

* run lint script instead of tsdx lint

* run prettier en-masse

This has a few changes because of the new prettier version.

* bump typescript to latest version

* make typescript happy

* cleanup playground package.json

* make esbuild a dev dependency

* make scripts consistent

* fix husky hooks

* add dedicated watch script

* add `yarn playground-react` and `yarn react-playground` (alias)

This will make sure to run a watcher for the actual @headlessui/react
package, and start a development server in the playground-react package.

* ignore formatting in the .next folder

* run prettier on playground-react package

* setup playground-vue

Still not 100% working, but getting there!

* add playground aliases in @headlessui/vue and @headlessui/react

This allows you to run `yarn react playground` or `yarn vue playground`
from the root.

* add `clean` script

* move examples folder in playground-vue to root

* ensure new lines for consistency in scripts

* fix typescript issue

* fix typescript issues in playgrounds

* make sure to run prettier on everything it can

* run prettier on all files

* improve error output

If you minify the code, then it could happen that the errors are a bit
obscure. This will hardcode the component name to improve errors.

* add the `prettier-plugin-tailwindcss` plugin, party!

* update changelog
2022-01-27 17:07:38 +01:00
Robin Malfait 3855d664ea Link to docs (#333)
* increase maximum error offset for CI tests

We try to detect how long durations took. However there is no nice way
to time this in JSDOM. Instead we take snapshots every
requestAnimationFrame and when things change we also write down the
time.

This solution is not ideal and results in false positives (especially on
CI environments).

However, it is good enough to ensure that the duration is not 0 and not
500.

* cleanup README's and link to docs site

* remove readme's in favor of doc site

This will be easier, so that we don't have to maintain multiple repo's.
2021-04-14 18:36:38 +02:00
Robin Malfait 4942928db1 Improve transition naming (#331)
* rename Transition to TransitionRoot

This will allow us to write it as:

- TransitionRoot
- TransitionChild

This has the added benefit that it doesn't collide with the internal
Transition component from Vue itself.

* alias Transition.Root to Transition

This allows us to write:

- Transition.Root
- Transition.Child

If you have a standalone Transition, then you can still use <Transition /> as is.

* drop unusued import

* update changelog
2021-04-13 19:01:07 +02:00
Robin Malfait 0a39cf6b22 Transition component (#326)
* add redent function when verifying snapshots

This allows us not to care about the correct amount of spaces and always
produces a clean output.

* make the container the parent of the wrapper element

* drop the visible prop on the Portal component

* drop visible prop on Portal component

+ Also cleanup a little bit

* expose the RenderStrategy

* implement Transition component in Vue

* expose Transition component

* add Transitions to the Dialog example
2021-04-12 23:40:42 +02:00
Robin Malfait ef00732685 cleanup and consistency (#213)
- Made the use of `const` and `let` consistent
- import required functions and types from 'react' instead of using the
  `React.` namespace.
- Added `Expand` type, which can expand complex types to their "final"
  result.
- Ensured that we use `as const` for DEFAULT_XXX_TAG where we used a
  string. So that we have the type of `div` instead of `string` for
  example.
- Used `interface` over `type` where possible. I'm personally more of a
  `type` fan. But the TypeScript recommends `interfaces` where possible
  because they are faster, yield better error messages and so on.
2021-01-30 14:46:54 +01:00
Robin Malfait aab23c9077 feat: add render features + render strategy (#106)
* add unmount strategy to README (React)

* add unmount strategy to README (Vue)

* add different render features (React)

* use render features in Menu and Listbox (React)

* add different render features (Vue)

* use render features in Menu and Listbox (Vue)

* bump dependencies

* add ability to change the ref property using `refName`

Example use case:

```tsx
// Some components have this API with an `innerRef`. The suggested approach is to use
// `React.forwardRef` so that you get the actual `ref` value. However if you already have this
// `innerRef` API than we can use the `refName="innerRef"` to give the `ref` prop a good name. It
// defaults to `ref` so that it still works everywhere else.

function MyButton({ innerRef, ...props }) {
  return <button ref={innerRef} {...props} />
}

<Menu.Button as={MyButton} refName="innerRef" />
```

* small cleanup, move refs to props we control

* add tests for the render abstraction (Render)

+ use the unique __ symbol as a default value in the Props type for the
  omitable props.

* use render features in Transition (React)

* add/update Transition examples to also showcase the `unmount={false}` render strategy

* bump dependencies

* add example with nested unmount/hide transitions

* add unmount to Transition documentation
2020-10-18 15:34:05 +02:00
Robin Malfait a5089d07b1 feat: Add Transition events (#57)
* fix wrong class in tests

* add Transition event callbacks

* add Transition Modal example with Event callbacks

* update props table of Transition component
2020-10-08 15:30:10 +02:00
Robin Malfait 8b546f054b setup @headlessui/react package 2020-09-16 18:20:49 +02:00