Commit Graph

295 Commits

Author SHA1 Message Date
Jordan Pittman 10f932afef Add <fieldset disabled> check to radio group options in React (#1835)
* wip

* Update changelog
2022-09-08 10:55:17 -04:00
Robin Malfait 397ba5c8c2 Improve iOS scroll locking (#1830)
* use a simpler `position: fixed` approach to prevent scroll locking

This isn't super ideal, but just preventing the default behavior on the
entire document while `touchmove`-ing isn't ideal either because then
you can't scroll inside the dialog or on the backdrop if your dialog
panel is larger than the viewport.

Again, this is not 100% correct, but it is better because you will be
able to scroll the dialog, and not the body.

* update changelog
2022-09-07 12:32:46 +02:00
Robin Malfait b93b7463bc 1.7.0 2022-09-06 16:22:42 +02:00
Jonathan Reinink dd8cfb427f Tweak changelogs 2022-09-06 10:15:13 -04:00
Robin Malfait 5667e84f35 Fix "blank" screen on initial load of Transition component (#1823)
* use a "cancellable" microTask

* update changelog
2022-09-06 13:37:55 +02:00
Robin Malfait 0954ec5243 Fix maximum call stack size exceeded error on Tab component when using as={Fragment} (#1826) 2022-09-06 12:35:24 +02:00
Robin Malfait df5b6a288c fix incorrect links in CHANGELOGs 2022-09-06 11:53:30 +02:00
Robin Malfait 25a4e7f721 Improve scroll lock on iOS (#1824)
* improve `Dialog` scroll lock on iOS

* add Dialog example to playground that's scrollable

* update changelog
2022-09-05 23:54:54 +02:00
Robin Malfait 1fd8cfcad0 Expose the value from the Combobox and Listbox components render prop (#1822)
* expose the `value` for `Combobox` and `Listbox`

* update changelog
2022-09-05 16:11:12 +02: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
Jordan Pittman 13463f0295 Fix nullable by prop for combobox and radio group (#1815)
* Fix nullable by prop for combobox and radio group

* Update changelog
2022-09-02 16:10:10 -04:00
Jordan Pittman 5165be596f Allow the by prop (as a string) to handle null values (#1814)
* Allow the by prop (as a string) to handle null values

* Update changelog
2022-09-02 14:49:33 -04:00
Robin Malfait 4878092712 Improve accessibility when announcing Listbox.Option and Combobox.Option components (#1812)
* ensure that `aria-selected` is explicitly set to `false`

The WAI-ARIA Best Practices don't recommend this and prefer
`aria-selected: true` or undefined (aka not existing when it is
"false"). However in practice, both MacOS VoiceOver and NVDA experience
strange issues if you don't do this (e.g.: everything before the
selected item is also selected)

* update tests to ensure we are checking for `aria-selected=false`

* update changelog
2022-09-02 16:31:46 +02:00
Robin Malfait ed4d80e442 update changelog 2022-09-02 15:07:32 +02:00
Robin Malfait 0c0a15a6d8 Ensure enter transitions work when using unmount={false} (#1811)
* ensure we never start transitioning while the node is still hidden

* update changelog
2022-09-02 14:50:49 +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
Jordan Pittman 920365c1b7 Stop scrolling when hitting end of focus trap (#1789)
* wip

* wip

* fix it

* fixit

* update changelog
2022-08-26 09:26:09 -04:00
Robin Malfait b301f04c77 Only restore focus to the Menu.Button if necessary when activating a Menu.Option (#1782)
* only restore focus to the Menu Button if necessary

This will check whether the focus got moved to somewhere else or not
once we activate an item via click or pressing `enter`.

Pressing escape will still move focus to the Menu Button.

* update changelog
2022-08-22 17:00:15 +02:00
Robin Malfait 486ac8075d Improve the types of the Combobox component (#1761)
* improve types of `Combobox`

Now given the `multiple` and/or `nullable` props we ensure that the
types for the `value`, `defaultValue`, `onChange`, `by`, render prop,
... are all correct.

You will also be able to easily tell which type to use instead of
inferring it by doing something like this:

```tsx
<Combobox<ExplicitTypeHere>
  value={...}
  onChange={...}
  ...
>
 ...
</Combobox>
```

* update changelog
2022-08-11 23:03:56 +02:00
Jordan Pittman b28d177a95 Fix displayValue syncing problem (#1755)
* ensure `syncInputValue` is updated correctly

* WIP

* WIP

* Don’t resync on open

* Fix react value syncing

update

* Add comment

* Port new setup over to Vue

* Remove `inputPropsRef`

We hardly knew ye

* Remove repro

* Cleanup

* Update changelog

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2022-08-10 12:38:30 -04:00
Robin Malfait 122eed7dbe Only select the active option when using "singular" mode (#1750)
* only select the active option when using "singular" mode

* update changelog
2022-08-09 16:38:29 +02:00
Robin Malfait 4c433cdf5c Ensure Disclosure.Panel is properly linked (#1747)
* ensure `Disclosure.Panel` is properly linked

* update changelog
2022-08-09 13:38:15 +02:00
Markus Baumer fa10cb0fb9 fixes tailwindlabs/headlessui#1733 (#1734) 2022-08-03 21:16:49 +02:00
Eddy Sims db736d8c37 Allow MouseEventHandler types to passed close from Popovers (#1696)
* fix: adds the MouseEventHandler type to close

* WIP

* WIP

* Update changelog

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2022-08-02 16:01:27 -04:00
Robin Malfait 4d88dd9d7e Improve Combobox re-opening keyboard issue on mobile (#1732)
* prevent re-focusing Combobox Input after choosing selection

On mobile this gives a bit of annoying results where after choosing an
option, the keyboard is shown again because the input is focused again.

* update changelog
2022-08-01 15:27:39 +02:00
Robin Malfait cbb4f19fb3 update changelog 2022-08-01 12:38:49 +02:00
Robin Malfait 1831832458 Make form components uncontrollable (#1683)
* implement uncontrolled form components

A few versions ago we introduced compatibility with the native `form`
element. This means that behind the scenes we render hidden inputs that
are kept in sync which allows you to submit your normal form and get
data via `new FormData(event.currentTarget)`.

Before this change every form related component (Switch, RadioGroup,
Listbox and Combobox) always had to be passed a `value` and an
`onChange` regardless of this change.

This change will allow you to not even use the `value` and the
`onChange` at all and keep it completely uncontrolled.

This has some changes:

- `value` is made optional
- `onChange` is made optional (but will still be called if passed
  regardless of being controlled or uncontrolled)
- `defaultValue` got added so that you can still pre-fill your values
  with known values.
- `value` render prop got exposed so that you can still use this while
  rendering.

This should also make it completely compatible with tools like Remix
without wiring up your own state.

* update example combinations/form playground to use uncontrolled
components

* improve types, add missing render prop arguments

* add tests for uncontrolled components (React)

* implement uncontrolled form elements in Vue
2022-08-01 12:37:50 +02:00
Jordan Pittman 6ecd448c48 Don’t overwrite element.focus on popover panels (#1719)
* Don’t overwrite `element.focus` on popover panels

* Update changelog

* Add test

This test isn’t exactly right for JSDOM but it does mirror what we would do in the browser to reproduce the problem
2022-07-26 16:06:10 -04:00
Robin Malfait 6598db59b7 Ensure by comparison is used in multiple mode (#1717)
* use the `compare` function in multiple mode

* add tests to verify fix of incorrect `by` behaviour

* improve TypeScript types for the `by` prop

* update changelog
2022-07-26 17:34:14 +02:00
Robin Malfait f2c2d3c4e0 Fix incorrect scrolling to the bottom when opening a Dialog (#1716)
* add `?raw` option to playground

This will render the component as-is without the wrapper.

* delay initial focus and make consistent between React and Vue

This will delay the initial focus and makes it consistent between React
and Vue.

Some explanation from within the code why this is happening:

   Delaying the focus to the next microtask ensures that a few
   conditions are true:

   - The container is rendered
   - Transitions could be started

   If we don't do this, then focusing an element will immediately cancel
   any transitions. This is not ideal because transitions will look
   broken. There is an additional issue with doing this immediately. The
   FocusTrap is used inside a Dialog, the Dialog is rendered inside of a
   Portal and the Portal is rendered at the end of the `document.body`.
   This means that the moment we call focus, the browser immediately
   tries to focus the element, which will still be at the bodem
   resulting in the page to scroll down. Delaying this will prevent the
   page to scroll down entirely.

* update test to reflect initial focus delay

Now that we are triggering the initial focus inside a `queueMicroTask`
we have to make sure that our tests wait a frame so that the micro task
could run, otherwise we will have incorrect results.

Also make the implementation similar in React and Vue

* update changelog
2022-07-26 15:29:49 +02:00
Robin Malfait 42db5b02d5 Improve event handler merging (#1715)
* improve event handler merging

This will ensure that an actual event is passed before checking the
`event.defaultPrevented`.

For React, we also have to make sure that we are not dealing with a
SyntehticEvent.

Thanks @Mookiepiece!

Co-authored-by: =?UTF-8?q?=E5=BD=BC=E8=A1=93=E5=90=91?= <48076971+Mookiepiece@users.noreply.github.com>

* update changelog

Co-authored-by: =?UTF-8?q?=E5=BD=BC=E8=A1=93=E5=90=91?= <48076971+Mookiepiece@users.noreply.github.com>
2022-07-26 12:40:06 +02:00
Robin Malfait b2c4023731 Improve outside click on Safari iOS (#1712)
* ensure outside click works on Safari in iOS

When tapping on an element that is not clickable (like a div), then the
`click` and `mousedown` events will not reach the
`window.addEventListener('click')` listeners.

The only event that does that could be interesting for us is the
`pointerdown` event. The issue with this one is that we then run into
the big issue we ran in a few months ago where clicks on a scrollbar
*also* fired while a click doesn't.

This issue was not an issue in React land, the
`window.addEventListener('click')` was fired even when tapping on a
`div`. This was very very confusing, but we think this is because of the
syntethic event system, where the event listener is added to the root of
your application (E.g.: #app) and React manually bubbles the events.
Because this is done manually, it *does* reach the window as well.

The confusing part is, how does React convert a `pointerdown` event to a
`mousedown` and `click`. There is no code for that in their codebase?

Turns out they don't, and turns out the events **do** bubble, but up
until the `document`, not the `window`. But since they are manually
bubbling events it all makes sense.

So the solution? Let's switch from `window` to `document`...

* update Dialog example to use DialogPanel

* update changelog
2022-07-26 11:40:21 +02:00
Jordan Pittman 5af3bd4b71 Don't scroll lock when a Transition + Dialog is mounted but hidden (#1681)
* Refer to context for initial Transition Tree state

* Update changelog
2022-07-15 11:43:45 -04:00
Robin Malfait 6d13e7958e Ensure controlled Tabs don't change automagically (#1680)
* fix controlled tabs should not switch tabs

When the `Tabs` component is used ina a controlled way, then clicking on
a tab should call the `onChange` callback, but it should not change the
actual tab internally.

* update changelog
2022-07-15 16:54:56 +02:00
Jordan Pittman cf78d97716 Resync input when display value changes (#1679)
* Resync input when display value changes

* Update changelog
2022-07-15 10:24:02 -04:00
Robin Malfait 0e1599b464 Close Menu component when using tab key (#1673)
* menu should not trap focus for tab key

* introduce `focusFrom` focus management utility

This is internal API, and the actual API is not 100% ideal. I started
refactoring this in a separate branch but it got out of hand and touches
a bit more pieces of the codebase that aren't related to this PR at all.

The idea of this function is just so that we can go Next/Previous but
from the given element not from the document.activeElement. This is
important for this feature. We also bolted this ontop of the existing
code which now means that we have this API:

```js
focusIn([], Focus.Previouw, true, DOMNode)
```

Luckily it's internal API only!

* ensure closing via Tab works as expected

Just closing the Menu isn't 100% enough. If we do this, it means that
when the Menu is open, we press shift+tab, then we go to the
Menu.Button because the Menu.Items were the active element.

The other way is also incorrect because it can happen if you have an
`<a>` element as one of the Menu.Item elements then that `<a>` will
receive focus, then the `Menu` will close unmounting the focused `<a>`
and now that element is gone resulting in `document.body` being the
active element.

To fix this, we will make sure that we consider the `Menu` as 1 coherent
component. This means that using `<Tab>` will now go to the next element
after the `<Menu.Button>` once the Menu is closed.

Shift+Tab will go to the element before the `<Menu.Button>` even though
you are currently focused on the `Menu.Items` so depending on the timing
you go to the `Menu.Button` or not.

Considering the Menu as a single component it makes more sense use the
elements before / after the `Menu`

* update changelog

Co-authored-by: Enoch Riese <enoch.riese@gmail.com>
2022-07-14 21:57:58 +02:00
Jordan Pittman f1daa1e52b Adjust outside click handling (#1667)
* Don’t close dialog if opened during mouse up event

* Don’t close dialog if drag starts inside dialog and ends outside dialog

* Handle closing of nested dialogs that are always mounted

* Fix focus trap restoration in Vue

* Update changelog
2022-07-14 14:20:04 -04:00
Sean Aye 6119cc202d Fix SSR support in Deno (#1671)
* check typeof document in addition to typeof window

* remove unused import

* Extract SSR check to a central spot

* Fix CS

* Update changelog

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2022-07-14 12:30:33 -04:00
Robin Malfait a294fdbc6c Revert "prepare 1.6.7"
This reverts commit 9807e2ba7e.
2022-07-12 16:04:24 +02:00
Robin Malfait 9807e2ba7e prepare 1.6.7 2022-07-12 15:59:01 +02:00
Robin Malfait fc7def3295 Revert "prepare 1.6.6"
This reverts commit 06df02a158.
2022-07-07 23:06:18 +02:00
Robin Malfait 3799d6f438 1.6.6 2022-07-07 23:02:52 +02:00
Robin Malfait 06df02a158 prepare 1.6.6 2022-07-07 22:57:45 +02:00
Robin Malfait 255fc36668 Ensure PopoverPanel can be used inside <transition> (#1653)
* ensure there is an animatable root node

This is a bit sad, but it is how Vue works...

We used to render just a simple PopoverPanel that resolved to let's say
a `<div>`, that's all good. Because the native `<transition>` component
requires that there is only 1 DOM child (regardless of the Vue "tree").
This is the sad part, because we simplified focus trapping for the
Popover by introducing sibling hidden buttons to capture focus instead
of managing this ourselves.

Since we can't just return multiple items we wrap them in a `Fragment`
component.
If you wrap items in a Fragment, then a lot of Vue's magic goes away
(automatically adding `class` to the root node). Luckily, Vue has a
solution for that, which is `inheritAttrs: false` and then manually
spreading the `attrs` onto the correct element.

This all works beautiful, but not for the `<transition>` component...
so... let's move the focus trappable elements inside the actual Panel
and update the logic slightly to go to the Next/Previous item instead of
the First/Last because the First/Last will now be the actual focus guards.

* update changelog

* make TypeScript a bit happier

* improve `default` slot in `PopoverPanel`
2022-07-07 18:34:39 +02:00
Robin Malfait 0260afa2df Properly merge incoming props with own props (#1651)
* sort props in error message

This will make the error message consistent regardless which props (and
in what order) they are applied.

* WIP

* `click()` on a disabled element should no-op

* incomingProps was already merged

* cleanup tests a bit and make it consistent with the React tests

* cleanup unused code

* update changelog
2022-07-07 17:01:45 +02:00
Robin Malfait 65bbacd894 Ensure CMD+Backspace works in nullable mode for Combobox component (#1617)
* ensure cmd+backspace works

The issue is that cmd+backspace technically already does work, but we
only allowed it when the Combobox is in an open state. We can remove
this check and apply the proper logic always.

* update changelog
2022-06-24 15:50:38 +02:00
Robin Malfait bc0b64a8c9 Revert "prepare 1.6.5"
This reverts commit c31136032b.
2022-06-20 18:00:31 +02:00
Robin Malfait f4d1acb5d7 1.6.5 2022-06-20 17:57:56 +02:00
Robin Malfait c31136032b prepare 1.6.5 2022-06-20 16:22:05 +02:00
Robin Malfait 63417d5fd9 Fix missing aria-expanded for ComboboxInput component (#1605)
* add test to verify `Combobox.Input` state

* incorrect missing `aria-expanded` on `ComboboxInput`

* update changelog
2022-06-20 11:53:45 +02:00