* compute `selectedOptionIndex` when using `anchor="selection"`
Instead of relying on the DOM directly, we can compute the
`selectedOptionIndex` and rely on the data directly.
We will also freeze the value while closing to prevent UI changes.
* update changelog
* add function to map transition data to data attributes
* use transition data attributes in props
Instead of in the `slot` because this would also expose this information
as render props but we just want to set it as props without exposing it
as render props.
* rename `slot` to `transitionData` for consistency
* update changelog
* add internal `Frozen` component and `useFrozenData` hook
* implement frozen state for the `Combobox` component
When the `Combobox` is in a closed state, but still visible (aka
transitioning out), then we want to freeze the `children` of the
`ComboboxOptions`. This way we still look at the old list while
transitioning out and you can safely reset any `state` that filters the
options in the `onClose` callback.
Note: we want to only freeze the children of the `ComboboxOptions`, not
the `ComboboxOptions` itself because we are still applying the necessary
data attributes to make the transition happen.
Similarly, if you are using the `virtual` prop, then we only freeze the
`virtual.options` and render the _old_ list while transitioning out.
* use `useFrozenData` in `Listbox` component
* use `data-*` attributes and `transition` prop to simplify playgrounds
* update changelog
* improve comment
* simplify frozen conditions
* use existing variable for frozen state
* add optional `start` and `end` events to `useTransitionData`
This will be used when we implement the `<Transition />` component
purely using the `useTransitionData` information. But because there is a
hierarchy between `<Transition />` and `<TransitionChild />` we need to
know when transitions start and end.
* implement `<Transition />` and `<TransitionChild />` on top of `useTransitionData()`
* update tests
Due to a timing issue bug, we updated the snapshot tests in
https://github.com/tailwindlabs/headlessui/pull/3273 incorrectly so this
commit fixes that which is why there are a lot of changes.
Most tests have `show={true}` but not `appear` which means that they
should _not_ transition which means that no data attributes should be
present.
* wait a microTask to ensure that `prepare()` has the time to render
Now that we set state instead of mutating the DOM directly we need to
wait a tiny bit and then we can trigger the transition to ensure
a smooth transition.
* cleanup `prepareTransition` now that it returns a cleanup function
* move `waitForTransition` and `prepareTransition` into `useTransitionData`
* remove existing `useTransition` hook and related utilities
* rename `useTransitionData` to `useTransition`
* update changelog
* Update packages/@headlessui-react/src/components/transition/transition.tsx
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* add missing `TransitionState.Enter`
This makes sure that the `Enter` state is applied initially when it has
to.
This also means that we can simplify the `prepareTransition` code again
because we don't need to wait for the next microTask which made sure
`TransitionState.Enter` was available.
* add transition playground page with both APIs
* update tests to reflect latest bug fix
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* allow `Tab` and `Shift+Tab` in `Listbox` component
This will make it consistent with the `Menu` and the ARIA Authoring
Practices Guide.
* update tests
* update changelog
* simplify `useFlags`
* add new `useTransitionData` hook
* use new `useTransitionData` hook
* add ability to cancel transitions mid-transition
* handle cancellations in both directions properly
* re-use existing `prepareTransition`
* expose `data-*` attributes for transitions in `<Transition />` component
* update tests to reflect added data attributes
* update changelog
* only call `getAnimations` if available
This has been around since 2020, but JSDOM doesn't know about this yet,
so tests using JSDOM will fail otherwise.
* use `flushSync` instead of `d.nextFrame`
This guarantees that after the `flushSync` call the DOM is updated. This
means that we don't have to guess and delay by a double
`requestAnimationFrame` (`nextFrame`) and _hope_ that the DOM was
updated already.
* inline disposables call
Each function in the `disposables()` object returns a cleanup function
which means we can return this directly.
* inline if-statements
Small one, but consistent with `<Menu />` and `<Listbox />` components.
* inline `flushSync()` callbacks
* merge incoming `style` prop
We were overriding the `style` prop entirely on the `<ComboboxOptions>`,
`<ListboxOptions>`, `<MenuItems>`, and `<PopoverPanel>` for anchoring
purposes, as well as provided some CSS variables.
This now ensures that the incoming `style` prop gets merged in.
* update changelog
* move `enabled` parameter in hooks to front
Whenever a hook requires an `enabled` state, the `enabled` parameter is
moved to the front. Initially this was the last argument and enabled by
default but everywhere that we use these hooks we have to pass a
dedicated boolean anyway.
This makes sure these hooks follow a similar pattern. Bonus points
because Prettier can now improve formatting the usage of these hooks.
The reason why is because there is no additional argument after the
potential last callback.
Before:
```ts
let enabled = data.__demoMode ? false : modal && data.comboboxState === ComboboxState.Open
useInertOthers(
{
allowed: useEvent(() => [
data.inputRef.current,
data.buttonRef.current,
data.optionsRef.current,
]),
},
enabled
)
```
After:
```ts
let enabled = data.__demoMode ? false : modal && data.comboboxState === ComboboxState.Open
useInertOthers(enabled, {
allowed: useEvent(() => [
data.inputRef.current,
data.buttonRef.current,
data.optionsRef.current,
]),
})
```
Much better!
* inline variables
* add `useDefaultValue` hook
This allows us to have a guaranteed `default value` that never changes
unless the component re-mounts.
Since the hook returns a stable value, we can safely include it in
dependency arrays of certain hooks.
Before this change, including this is in the dependency arrays it would
cause a trigger or change of the hook when the `defaultValue` changes
but we never want that.
* do not handle `reset` when no `defaultValue` or `defaultChecked` was provided
If a `defaultValue` is provided, then the reset will be handled and the
`onChange` will be called with this value.
If no `defaultValue` was provided, we won't handle the `reset`,
otherwise we would call the `onChange` with `undefined` which is
incorrect.
* update changelog
* Expose new components under dot notation
Most of them already where so only a couple were missing.
* Deprecate dot notation
* Add deprecation to `RadioGroupOption`
* Update deprecations
* Update changelog
* Update changelog
We keep specific types for elements with special meaning, such as
`HTMLButtonElement`, `HTMLLabelElement` or `HTMLInputElement` because
they receive certain attributes that generic DOM nodes (such as
HTMLDivElement) don't
For the components where we use simple `div` elements (and where people
use `as={...}`) that renders a different element, it doesn't make sense
to use `HTMLDivElement`. Using a more generic `HTMLElement` is simpler
and more correct (we still had `HTMLUListElement` and `HTMLLIElement`
for "div" DOM nodes which is incorrect).
This shouldn't be a breaking change because an `HTMLDivElement` is still
a valid `HTMLElement`. The other way around wouldn't be the case.
* use `act` from `react` instead of `@testing-library/react`
* bump dependencies
* bump `@testing-library/react`
* bump `@react-aria/interactions`
* bump "@tanstack/react-virtual"
* add `ResizeObserver` polyfill, and enable it by default for tests
* mock `getBoundingClientRect`
Otherwise the virtualization tests don't work as expected because they
rely on the client rect which is not supported (or not correctly
measured) in JSDOM.
* improve demo mode for the `Dialog` component
This still disabled `inert` and focus stealing code. But it does allow
outside click.
* improve demo mode for the `Combobox` component
Before this, once you start interacting with the `Combobox`, the options
weren't properly scrolled into view.
* improve demo mode for the `Menu` component
* improve demo mode for the `Popover` component
* add demo mode to the `Listbox` component
* move duplicated `useScrollLock` to dedicated hook
* accept `enabled` prop on `Portal` component
This way we can always use `<Portal>`, but enable / disable it
conditionally.
* use `useSyncRefs` in portal
This allows us to _not_ provide the ref is no ref was passed in.
* refactor inner workings of `useInert`
moved logic from the `useEffect`, to module scope. We will re-use this
logic in a future commit.
* add `useInertOthers` hook
Mark all elements on the page as inert, except for the ones that are allowed.
We move up the tree from the allowed elements, and mark all their
siblings as `inert`. If any of the children happens to be a parent of
one of the elements, then that child will not be marked as `inert`.
```
<body> <!-- Stop at body -->
<header></header> <!-- Inert, sibling of parent of allowed element -->
<main> <!-- Not inert, parent of allowed element -->
<div>Sidebar</div> <!-- Inert, sibling of parent of allowed element -->
<div> <!-- Not inert, parent of allowed element -->
<Listbox> <!-- Not inert, parent of allowed element -->
<ListboxButton></ListboxButton> <!-- Not inert, allowed element -->
<ListboxOptions></ListboxOptions> <!-- Not inert, allowed element -->
</Listbox>
</div>
</main>
<footer></footer> <!-- Inert, sibling of parent of allowed element -->
</body>
```
* add `portal` prop, and change meaning of `modal` prop on `MenuItems`
- This adds a `portal` prop that renders the `MenuItems` in a portal.
Defaults to `false`.
- If you pass an `anchor` prop, the `portal` prop will always be set
to `true`.
- The `modal` prop enables the following behavior:
- Scroll locking is enabled when the `modal` prop is passed and the
`Menu` is open.
- Other elements but the `Menu` are marked as `inert`.
* add `portal` prop, and change meaning of `modal` prop on `ListboxOptions`
- This adds a `portal` prop that renders the `ListboxOptions` in a
portal. Defaults to `false`.
- If you pass an `anchor` prop, the `portal` prop will always be set
to `true`.
- The `modal` prop enables the following behavior:
- Scroll locking is enabled when the `modal` prop is passed and the
`Listbox` is open.
- Other elements but the `Listbox` are marked as `inert`.
* add `portal` and `modal` prop on `ComboboxOptions`
- This adds a `portal` prop that renders the `ComboboxOptions` in a
portal. Defaults to `false`.
- If you pass an `anchor` prop, the `portal` prop will always be set
to `true`.
- The `modal` prop enables the following behavior:
- Scroll locking is enabled when the `modal` prop is passed and the
`Combobox` is open.
- Other elements but the `Combobox` are marked as `inert`.
* add `portal` prop, and change meaning of `modal` prop on `PopoverPanel`
- This adds a `portal` prop that renders the `PopoverPanel` in a portal.
Defaults to `false`.
- If you pass an `anchor` prop, the `portal` prop will always be set
to `true`.
- The `modal` prop enables the following behavior:
- Scroll locking is enabled when the `modal` prop is passed and the
`Panel` is open.
* simplify popover playground, use provided `anchor` prop
* remove internal `Modal` component
This is now implemented on a per component basis with some hooks.
* remove `Modal` handling from `Dialog`
The `Modal` component is removed, so there is no need to handle this in
the `Dialog`. It's also safe to remove because the components with
"portals" that are rendered inside the `Dialog` are portalled into the
`Dialog` and not as a sibling of the `Dialog`.
* ensure we use `groupTarget` if it is already available
Before this, we were waiting for a "next render" to mount the portal if
it was used inside a specific group. This happens when using `<Portal/>`
inside of a `<Dialog/>`.
* update changelog
* add tests for `useInertOthers`
* ensure we stop before the `body`
We used to have a `useInertOthers` hook, but it also made everything
inside `document.body` inert. This means that third party packages or
browser extensions that inject something in the `document.body` were
also marked as `inert`. This is something we don't want.
We fixed that previously by introducing a simpler `useInert` where we
explicitly marked certain elements as inert: https://github.com/tailwindlabs/headlessui/pull/2290
But I believe this new implementation is better, especially with this
commit where we stop once we hit `document.body`. This means that we
will never mark `body > *` elements as `inert`.
* add `allowed` and `disallowed` to `useInertOthers`
This way we have a list of allowed and disallowed containers. The
`disallowed` elements will be marked as inert as-is.
The allowed elements will not be marked as `inert`, but it will mark its
children as inert. Then goes op the parent tree and repeats the process.
* simplify `useInertOthers` in `Dialog` code
* update `use-inert` tests to always use `useInertOthers`
* remove `useInert` hook in favor of `useInertOthers`
* rename `use-inert` to `use-inert-others`
* cleanup default values for `useInertOthers`
* use `div` as default tag for `ListboxOptions` and `ListboxOption` components
* use `div` as default tag for `ComboboxOptions` and `ComboboxOption` components
* use `div` as default tag for `TabGroup`
* update changelog
* add `useOnDisappear` hook
This hook allows us to trigger a callback if the element becomes
"hidden". We use the bounding client rect and check the dimensions to
know wether we are "hidden" or not.
* use new `useOnDisappear` hook in components with the `anchor` prop
* update changelog
* document `useOnDisappear`
* expose `data-focus` on the `TabsPanel` component
* expose `data-disabled` on the `MenuButton` component
* expose `data-disabled` on the `PopoverButton` component
* expose `data-disabled` on the `DisclosureButton` component
* cleanup repetition
* update changelog
* add `satisfies` statements to ensure all data is present
* update changelog entry
* bump React & React DOM dependencies
* fix typo `TOmitableProps` → `TOmittableProps`
* bump prettier
* run prettier after prettier version bump
* bump TypeScript
* run prettier after TypeScript version bump
* enable `verbatimModuleSyntax`
This ensures all imported types are using the `type` keyword.
* add `type` to type related imports
* add common testing scenarios
Will be used in the new and existing components.
* add script to make Next.js happy
Right now Next.js does barrel file optimization and re-writing imports
to a real path in the `dist` folder. Most of those rewrites don't
actually exist because they have an assumption:
```js
import { FooBar } from '@headlessui/react'
```
is rewritten as:
```js
import { FooBar } from '@headlessui/react/dist/components/foo-bar/foo-bar'
```
This script will make sure these paths exist...
* improve `by` prop, introduce `useByComparator`
This hook has a default implementation when comparing objects. If the
object contains an `id`, then we will compare the objects by their
`id`'s without the user of the library needing to specify `by="id"`.
If the objects don't have an `id` prop, then the default is still to
compare by reference (unless specicified otherwise).
* sync yarn.lock
* rename `Features` to `HiddenFeatures` for `Hidden` component
* rename `Features` to `FocusTrapFeatures` in `FocusTrap` component
* rename `Features` to `RenderFeatures` in `render` util
* add `floating-ui` as a dependency + introduce internal floating related components
* bump Vue dependencies
* ensure scroll bar calculations can't go negative
* improve types in `@headlessui/vue`
* use snapshot tests for `Transition` tests in `@headlessui/vue`
* use snapshot tests for `portal` tests in `@headlessui/vue`
* rename `src/components/transitions/` to `src/components/transition/` (singular)
This is so that we can be consistent with the other components.
* drop custom `toMatchFormattedCss`, prefer snapshot tests instead
* use snapshot tests for `Label` tests in `@headlessui/vue`
* use snapshot tests for `Description` tests in `@headlessui/vue`
* sort exported components in tests for `@headlessui/vue`
* use snapshot tests in `@headlessui/tailwindcss`
* rename `mergeProps` to `mergePropsAdvanced`
This is a more complex version of a soon to be exported `mergeProps`
that we will be using in our components.
* do not expose `aria-labelledby` if it is only referencing itself
* expose boolean state as `kebab-case` instead of `camelCase`
These are the ones being exposed inside `data-headlessui-state="..."`
* expose boolean data attributes
A slot with `{active,focus,hover}` will be exposed as:
```html
<span data-headlessui-state="active focus hover"></span>
```
But also as boolean attributes:
```html
<span data-active data-focus data-hover></span>
```
* improve internal types for `className` in `render` util
* ensure we keep exposed data attributes into account when trying to forward them to the component inside the `Fragment`
* add small typescript type fix
This is internal code, and the public API is not influenced by this
`:any`. It does make TypeScript happy.
* introduce `mergeProps` util to be used in our components
This will help us to merge props, when event handlers are available they
will be merged by wrapping them in a function such that both (or more)
event handlers are called for the same `event`.
* add new internal `Modal` component
* fix: when using `Focus.Previous` with `activeIndex = -1` should start at the end
* prefer `window.scrollY` instead of `window.pageYOffset`
Because `window.pageYOffset` is deprecated.
* add `'use client'` directives on client only components
These components use hooks that won't work in server components and you
will receive an error otherwise.
* drop `import 'client-only'` in favor of the `'use client'` directive
* add React Aria dependencies
* pin beta dependencies
* prettier bump formatting
* improve TypeScript types in tests
* use new Jest matchers instead of deprecated ones
* improve typescript types in Vue
* prefer `useLabelledBy` and `useDescribedBy`
* add internal `DisabledProvider`
* add internal `IdProvider`
* add internal `useDidElementMove` hook
* add internal `useElementSize` hook
* add internal `useIsTouchDevice` hook
* add internal `useActivePress` hook
* use snapshot tests for `Description` tests
* use snapshot tests for `Label` tests
* use snapshot tests for `Portal` tests
* use snapshot tests for `render` tests
* add (private) `Tooltip` component
Currently this one is not ready yet, so its not publicly exposed yet.
* add internal `FormFields` component
This one adds a component to render (hidden) inputs for native form
support. It also ensures that form fields can be hoisted to the end of
the nearest `Field`. If the components are not inside a `Field` they
will be rendered in place.
* add new `Button` component
* add new `Checkbox` component
* add new `DataInteractive` component
* add new `Field` component
* add new `Fieldset` component
* add new `Legend` component
* add new `Input` component
* add new `Select` component
* add new `Textarea` component
* export new components
* WIP
* remove `within: true`
This only makes sense if anything inside the current element receives
focus, which is not the case for `input`, `select`, `textarea` or
`Radio/RadioOption`.
* group focus/hover/active hooks together
* conditionally link anchor panel
* immediately focus the container
* prevent premature disabling of `Listbox`'s floating integration
+ Track whether the button moved or not when disabling such that we can
disable the transitions earlier.
* improve scroll locking on iOS
* skip hydration tests for now
* skip certain focus trap tests for now
* update CHANGELOG.md
* add missing requires
* drop unused `@ts-expect-error`
* ignore type issues in playgrounds
These playgrounds are mainly test playgrounds. Lower priority for now,
we will get back to them.
* add yarn resolutions to solve swc bug
* fix VoiceOver bug for Listbox in Chrome
Chrome currently has a bug if you use a `Listbox` with a `Label` and use
the `aria-multiselectable` attribute. This combination will cause
VoiceOver to _not_ announce the `role="option"` elements when
interacting with them.
If we drop the `aria-multiselectable` OR the `aria-labelledby` it starts
working. Alternatively replacing `aria-labelledby` with `aria-label`
won't work either.
I filed a Chrome bug report about this here: https://bugs.chromium.org/p/chromium/issues/detail?id=1498261
---
Luckily there is a workaround in our `Listbox` implementation. Right now
we always require the `Listbox.Button` to be there. The
`Listbox.Options` component doesn't work on its own in our
implementation.
This means that whenever we open the `Listbox` that we have to go via
the `Listbox.Button`. This `Listbox.Button` is already labelled by the
`Listbox.Label` if there is one.
This also means that we can safely drop the `id` of the label inside the
`aria-labelledby` from the `Listbox.Options`.
This wouldn't have worked if our `Listbox.Options` could be used in a
standalone way without the `Listbox.Button`.
At the end of the day the hierarchy looks like this:
- Options is labelled by the Button
- Button is labelled by the Label
- Label
Fixes: #2817
* update changelog
* type timezones in playground data
* add `@tanstack/react-virtual` and `@tanstack/vue-virtual`
* use latest stable Tailwind CSS version
* add Combobox with virtual prop example
* add `virtual` prop to `Combobox`
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* add tests for `virtual` prop
- Also wrap `click` helpers in `act` for React (use `rawClick` without
`act` in tests related to `Transition`)
* update changelog
---------
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* add `prettier-plugin-organize-imports` and `prettier-plugin-tailwindcss`
* format
* bump Tailwind CSS
* format playgrounds using updated Tailwind CSS and Prettier plugins
* use import syntax
* export component interfaces, and mark them as internal
This is not ideal because we don't want these to be public. However, if
you are creating components on top of Headless UI, the TypeScript
compiler needs access to them.
So now they are public in a sense, but you shouldn't be interacting with
them directly.
Co-authored-by: Jordan Pittman <jordan@cryptica.me>
* Update changelog
---------
Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
* Fix bug with non-controlled, multiple combobox in Vue
It thought it was always controlled which broke things
* Use correct value when resetting `<Listbox multiple>` and `<Combobox multiple>`
* Update changelog
* define `aria-expanded` based on open/closed state
You shouldn't be able to open a Listbox/Menu/Combobox/... when the
component is in a disabled state, however if you open it, and then
disable it then it is still in an open state. Therefore the
`aria-expanded` should still be present.
This is also how other libraries behave.
It is also how the native `<select>` behaves. You can open it, disable
it programmatically and then you are still able to make a selection.
This seems enough evidence that this change is an improvement without
being a breaking change.
Fixes: #2602
* update changelog
* add `get-text-value` helper
* use `getTextValue` in `Listbox` component
* use `getTextValue` in `Menu` component
* update changelog
* ensure we handle multiple values for `aria-labelledby`
* hoist regex
* drop child nodes instead of replacing its innerText
This makes it a bit slower but also more correct. We can use a cache on
another level to ensure that we are not creating useless work.
* add `useTextValue` to improve performance of `getTextValue`
This will add a cache and only if the `innerText` changes, only then
will we calculate the new text value.
* use better `useTextValue` hook
* cleanup `XYZPropsWeControl`
The idea behind the `PropsWeControl` is that we can omit all the fields
that we are controlling entirely. In this case, passing a prop like
`role`, but if we already set the role ourselves then the prop won't do
anything at all. This is why we want to alert the end user that it is an
"error".
It can also happen that we "control" the value by default, but keep
incoming props into account. For example we generate a unique ID for
most components, but you can provide your own to override it. In this
case we _don't_ want to include the ID in the `XYZPropsWeControl`.
Additionally, we introduced some functionality months ago where we call
event callbacks (`onClick`, ...) from the incoming props before our own
callbacks. This means that by definition all `onXYZ` callbacks can be
provided.
* improve defining types
Whenever we explicitly provide custom types for certain props, then we
make sure to omit those keys first from the original props (of let's say
an `input`). This is important so that TypeScript doesn't try to "merge"
those types together.
* cleanup: move `useEffect`
* add `defaultValue` explicitly
* ensure tests are not using `any` because of `onChange={console.log}`
The `console.log` is typed as `(...args: any[]) => void` which means
that it will incorrectly mark its incoming data as `any` as well.
Converting it to `x => console.log(x)` makes TypeScript happy. Or in
this case, angry since it found a bug.
This is required because it _can_ be that your value (e.g.: the value of
a Combobox) is an object (e.g.: a `User`), but it is also nullable.
Therefore we can provide the value `null`. This would mean that
eventually this resolves to `keyof null` which is `never`, but we just
want a string in this case.
```diff
-export type ByComparator<T> = (keyof T & string) | ((a: T, b: T) => boolean)
+export type ByComparator<T> =
+ | (T extends null ? string : keyof T & string)
+ | ((a: T, b: T) => boolean)
```
* improve the internal types of the `Combobox` component
* improve the internal types of the `Disclosure` component
* improve the internal types of the `Listbox` component
* improve the internal types of the `Menu` component
* improve the internal types of the `Popover` component
* improve the internal types of the `Tabs` component
* improve the internal types of the `Transition` component
* use `Override` in `Hidden` as well
* cleanup unused code
* don't check the `useSyncExternalStoreShimClient`
* don't check the `useSyncExternalStoreShimServer`
* improve types in the render tests
* fix `Ref<TTag>` to be `Ref<HTMLElement>`
* improve internal types of the `Transition` component (Vue)
+ add `attrs.class` as well
* use different type for `AnyComponent`
* update changelog
* introduce `opening` and `closing` states
Also represent them as bits so that we can easily combine them while we
are transitioning from one state to the other.
* update `open/closed` state checks
Instead of checking whether it is in one state or an other, we can check
if the current state contains some potential sub-state.
This allows us to still check if we are in the `Open` state, while also
`Closing` because the state will be `S.Open | S.Closing`.
* expose `flags` from the `useFlags` hook
* add the `Closing` and `Opening` states to the Open/Closed state
* create dedicated `abcEnabled` variables
* keep the `State.Closing` into account for `scroll locking` and `inert others`
* add a test for the `Closing` state impacting the `Dialog` component
* cleanup unused imports
* add `unmount` util to the Vue Test renderer
* update changelog
* use the `import * as React from 'react'` pattern
We use named imports, but we have to import `React` itself as well for
JSX because it compiles to `React.createElement`. We could get rid of
our own JSX and use it directly, or we can use this `import * as React
from 'react'` syntax.
This fixes an issue for people using `allowSyntheticDefaultImports: false` in TypeScript.
Fixes: #2117
* update changelog
`aria-haspopup` should now contain the corresponding role instead of
just true or false. The `aria-haspopup="true"` is considered a `menu`
now.
Context: https://w3c.github.io/aria/#aria-haspopupFixes: #2099
* accept `id` as a prop where it is currently hardcoded (React)
Continuation of #2020
Co-authored-by: Olivier Louvignes <olivier@mgcrea.io>
* accept `id` as a prop where it is currently hardcoded (Vue)
* update changelog
* apply React's hook rules
Co-authored-by: Olivier Louvignes <olivier@mgcrea.io>
* add reset button to form example
* refactor React Listbox
This splitsup the raw `[state, dispatch]` to separate `useActions` and `useData` hooks.
This allows us to make the actions themselves simpler and include logic
that doesn't really belong in the reducer itself.
This also allows us to expose data via the `useData` hook that doesn't
belong in the state exposed from the `useReducer` hook.
E.g.: we used to store a `propsRef` from the root `Listbox`, and update
the ref with the new props in a `useEffect`. Now, we will just expose
that information directly via the `useData` hook. This simplifies the
code, removes useEffect's and so on.
* refactor Tabs, ensure function reference stays the same
If the `isControlled` value changes, then the references to all the
functions changed. Now they won't because of the `useEvent` hooks.
* type the actions abg similar to how we type the data bag
* refactor RadioGroup to use useData/useActions hooks
* reset Listbox to defaultValue on form reset
* reset Combobox to defaultValue on form reset
* reset RadioGroup to defaultValue on form reset
* reset Switch to defaultChecked on form reset
* port combinations/form playground example to Vue
* update changelog