Commit Graph

66 Commits

Author SHA1 Message Date
Robin Malfait f2179f36c0 further improve import sorting 2023-09-11 19:09:53 +02:00
Robin Malfait 76dd10ea55 Sort imports automatically (#2741)
* 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
2023-09-11 18:36:30 +02:00
Timur Zurbaev fa952626c7 Add immediate prop to <Combobox /> for immediately opening the Combobox when the input receives focus (#2686)
* Allow to open combobox on input focus

* Close focused combobox with openOnFocus prop when clicking the button

* ensure tabbing through a few fields, doesn't result in an incorrectly selected item

When you have a fwe inputs such as:

```html
<form>
   <input />
   <input />
   <input />
   <Combobox>
      <Combobox.Input />
   </Combobox>
   <input />
   <input />
   <input />
</form>
```

Tabbing through this list will open the combobox once you are on the
input field. When you continue tabbing, the first item would be
selected. However, if the combobox is not marked as nullable, it means
that just going through the form means that we set a value we can't
unset anymore.

We still want to open the combobox, we just don't want to select
anything in this case.

* only `openOnFocus` if the `<Combobox.Input />` is focused from the
outside

If the focus is coming from the `<Combobox.Button />` or as a side
effect of selecting an `<Combobox.Option />` then we don't want to
re-open the `<Combobox />`

* update tests to ensure that the `Combobox.Input` is the active element

* order `handleBlur` and `handleFocus` the same way in Vue & React

* only select the active option when the Combobox wasn't opened by focusing the input field

* convert to `immediate` prop on the `Combobox` itself

* update changelog

* ensure we see the "relatedTarget" in Safari

Safari doesn't fire a `focus` event when clicking a button, therefore it
does not become the `document.activeElement`, and events like `blur` or
`focus` doesn't set the button as the `event.relatedTarget`.

Keeping track of a history like this solves that problem. We already had
the code for the `FocusTrap` component.

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2023-08-31 15:23:56 +02:00
Robin Malfait 134c0fb221 Ensure blurring the Combobox.Input component closes the Combobox (#2712)
* ensure blurring the `Combobox.Input` component closes the `Combobox`

* update changelog

* select the value on blur if we are in single value mode
2023-08-29 16:24:37 +02:00
Robin Malfait 88b068cff1 Improve resetting values when using the nullable prop on the Combobox component (#2660)
* move `nullable` handling to `onChange` of `Combobox.Input` itself

We were specifically handling backspace/delete keys to verify if the
`Combobox.Input` becomes empty then we can clear the value if we are in
single value and in nullable mode.

However, this doesn't capture other ways of clearing the
`Combobox.Input`, for example when use `cmd+x` or `ctrl+y` in the input.

Moving the logic, gives us some of these cases for free.

* ensure pressing `escape` also clears the input in nullable, single value mode without an active value

* adjust test to ensure we don't have a selected option instead of an active option

We still will have an active option (because we default to the first
option if nothing is active while the combobox is open). But since we
cleared the value when using the `nullable` prop, then it means the
`selected` option should be cleared.

* ensure `input` event is fired when firing keydown events

* ensure `defaultToFirstOption` is always set when going to an option

We recently made a Vue improvement that delayed the going to an option,
but this also included a bug where the `defaultToFirstOption` was not
set at the right time anymore.

* update changelog

* fix `than` / `then` typo
2023-08-08 19:09:01 +02:00
Jordan Pittman b380d03ccc Use correct value when resetting <Listbox multiple> and <Combobox multiple> (#2626)
* 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
2023-07-28 11:46:33 -04:00
Robin Malfait ac859fe6b1 Submit form on Enter even if no submit-like button was found (#2613)
* `requestSubmit` when a submit-like button cannot be found

* add tests

* update changelog
2023-07-25 15:48:35 +02:00
Robin Malfait 04fc6cfa06 Ensure the caret is in a consistent position when syncing the Combobox.Input value (#2568)
* ensure the caret is at the end of the input, after syncing the value

This will ensure the caret is always in a consistent location once the
input value has synced. We will _not_ do this while the user is typing
because changing the position while typing will result in odd results.

Safari already kept the caret at the end, Chrome put the caret at the
end but once you synced the value once the caret was in front of the
text.

* update changelog

* add selection guards

Ensure that we only move the caret to the end, if the selection didn't
change in the meantime yet. For example when you have code like this:
```js
<Combobox.Input onFocus={e => e.target.select()} />
```

This will select all the text in the input field, if we just move the
caret position without keeping this into account then we would undo this
behaviour.

* add tests
2023-06-30 12:58:44 +02:00
Mathieu TUDISCO d55c77e655 [vue] Fix Combobox input disabled state (#2375)
* [vue] Fix Combobox input disabled state

* Add tests for disabled input in React and Vue

* Update changelog

---------

Co-authored-by: Jordan Pittman <jordan@cryptica.me>
2023-03-16 19:15:22 -04:00
Arber Sylejmani fb612f7580 Add form prop to form-like components such as RadioGroup, Switch, Listbox, and Combobox (#2356)
* Adds form prop to Switch component

* add `form` prop to `Switch` component in Vue

+ tests for both React and Vue

* add `form` prop to `Combobox` component

* add `form` prop to `Listbox` comopnent

* add `form` prop to `RadioGroup` component

* update changelog

* add Oxford comma

* cleanup `screen` import

---------

Co-authored-by: Robin Malfait <malfait.robin@gmail.com>
2023-03-14 13:36:49 +01:00
Robin Malfait 989cd6b040 Fix XYZPropsWeControl and cleanup internal TypeScript types (#2329)
* 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
2023-03-02 22:50:41 +01:00
Jordan Pittman b8c214eebb Make React types more compatible with other libraries (#2282)
* Export explicit props types

* wip

* wip

* wip

* wip dialog types

* wip

* Fix build

* Upgrade esbuild

* Add aliased types for ComponentLabel and ComponentDescription

* Update lockfile

* Update changelog

* Update exported prop type names

* Make onChange optional

* Update tests

* Use `never` in CleanProps

Using a branded type doesn’t work properly with unions

* Fix types

* wip

* work on types

* wip

* wip

* Tweak types in render helpers

* Fix CS

* Fix changelog

* Tweak render prop types for combobox

* Update hidden props type name

* remove unused type

* Tweak types

* Update TypeScript version
2023-02-20 12:26:17 -05:00
Robin Malfait fcfd554514 Ensure we reset the activeOptionIndex if the active option is unmounted (#2274)
* ensure we reset the `activeOptionIndex` if the active option is unmounted

Unmounting of the active option can happen when you are in a
multi-select Combobox, and you filter out all the selected values. This
means that the moment you press "Enter" on an active item, it becomes
the selected item and therefore will be filtered out.

* update changelog
2023-02-10 19:54:58 +01:00
Robin Malfait 1f2de63b40 Fix displayValue syncing when Combobox.Input is unmounted and re-mounted in different trees (#2090)
* simplify `currentDisplayValue` calculation

Always calculate the currentDisplayValue, and only apply it if the user
is not typing. In all other cases it can be applied (e.g.: when the
value changes from the outside, inside or on reset)

* update changelog
2022-12-12 19:16:46 +01:00
Robin Malfait 46754e637c Fix regression where displayValue crashes (#2087)
* fix regression where `displayValue` crashes

It regressed in the sense that it now uses `displayValue` for the
`defaultValue` as well, but if nothing is passed it would crash.

Right now, it makes sure to only run the displayValue value on the
actual value and the actual default value if they are not undefined.

Note: if your displayValue is implemented like `(value) => value.name`,
and your `value` is passed as `null`, it will still crash (as expected)
because then you are in charge of rendering something else than null. If
we would "fix" this, then no value can be rendered instead of `null`.

Fixes: #2084

* update changelog
2022-12-12 16:43:57 +01:00
Robin Malfait a0bcbae93a Fix crash when using multiple mode without value prop (uncontrolled) for Listbox and Combobox components (#2058)
* ensure `value` in `multiple` mode is always an array

In case nothing or `undefined` was passed.

* update changelog
2022-12-02 15:18:31 +01: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 c0f0d43383 Reset form-like components when the parent <form> resets (#2004)
* 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
2022-11-09 23:39:23 +01:00
Jordan Pittman ab78fbd91e Fire user’s onChange handler when we update the combobox input value internally (#1916)
* Fire user’s onChange handler when we update the input value internally

* Update changelog

* Fix CS
2022-10-10 15:17:52 -04:00
Jordan Pittman 1127a55a76 Warn when changing Combobox between controlled and uncontrolled (#1878) 2022-10-10 10:41:15 -04:00
Jordan Pittman 060f37b667 Fix use of undefined and displayValue in Combobox (#1865)
* Work around Vue multi-source + undefined bug

* Update changelog
2022-09-19 09:56:57 -04:00
Robin Malfait 46a7ab67ab Ensure Combobox.Label is properly linked when rendered after Combobox.Button and Combobox.Input components (#1838)
* ensure `Combbox.Label` is properly linked when rendered after other components

Even when rendered after the Combobox.Input / Combobox.Button

* update changelog
2022-09-08 23:18:33 +02:00
Robin Malfait b296b73783 Ensure Tab order stays consistent, and the currently active Tab stays active (#1837)
* ensure tabs order stays consistent

This ensures that whenever you insert or delete tabs before the current
tab, that the current tab stays active with the proper panel.

To do this we had to start rendering the non-visible panels as well, but
we used the `Hidden` component already which is position fixed and
completely hidden so this should not break layouts where using flexbox
or grid.

* update changelog

* fix TypeScript issue
2022-09-08 19:14:42 +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
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
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 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 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
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
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 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 06df02a158 prepare 1.6.6 2022-07-07 22:57:45 +02:00
Robin Malfait bc0b64a8c9 Revert "prepare 1.6.5"
This reverts commit c31136032b.
2022-06-20 18:00:31 +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
Robin Malfait 1d53ac312f Revert "prepare 1.6.4"
This reverts commit 842d07146e.
2022-05-29 14:57:35 +02:00
Robin Malfait 842d07146e prepare 1.6.4 2022-05-29 14:51:38 +02:00
Robin Malfait eefc03ce16 Ensure Escape propagates correctly in Combobox component (#1511)
* ensure `Escape` propagates correctly in Combobox component

* update changelog
2022-05-27 17:57:28 +02:00
Robin Malfait 08b419e9b7 Revert "prepare 1.6.3"
This reverts commit 3aaf20b9ac.
2022-05-25 15:25:48 +02:00
Robin Malfait 3aaf20b9ac prepare 1.6.3 2022-05-25 15:13:09 +02:00
Robin Malfait d200be5f6f Add by prop for Listbox, Combobox and RadioGroup (#1482)
* Add `by` prop for `Listbox`, `Combobox` and `RadioGroup`

* update changelog
2022-05-20 23:01:10 +02:00
Robin Malfait 9280d92d24 Allow to override the type on the Combobox.Input (#1476)
* allow to override the `type` on the `Combobox.Input`

This still defaults to `text`.

* update changelog
2022-05-19 16:57:35 +02:00
Robin Malfait 0c34fe802c Add explicit multiple prop (#1355)
* add explicit `multiple` prop to the `Combobox`

This allows you to set the value to a **tuple** in `single-value` mode,
which was not possible before the `multiple` prop was introduced,
because then it resulted in `multi-value` mode instead of `single-value`
mode.

* add explicit `multiple` prop to the `Listbox`

This allows you to set the value to a **tuple** in `single-value` mode,
which was not possible before the `multiple` prop was introduced,
because then it resulted in `multi-value` mode instead of `single-value`
mode.

* update changelog

* update playground to use `multiple` prop
2022-04-22 18:55:55 +02:00
Robin Malfait b4a4e0b307 Add Dialog.Backdrop and Dialog.Panel components (#1333)
* implement `Dialog.Backdrop` and `Dialog.Panel`

* cleanup TypeScript warnings

* update changelog
2022-04-14 17:15:43 +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 ab6310c278 Implement nullable mode on Combobox in single value mode (#1295)
* implement `backspace` behaviour in tests

* add `Delete` Key

* implement `nullable` mode on Combobox in single value mode

If you pass a `nullable` prop to the Combobox, then it's possible to
unset the Combobox value by setting it to `null`.
This is triggered by removing all text from the input which will reset
the value itself as well.

* update changelog
2022-03-31 23:35:04 +02:00
Robin Malfait c475cab451 Allow Enter for form submit in RadioGroup, Switch and Combobox improvements (#1285)
* improve rendering of hidden form fields

* add `attemptSubmit` helper

This will allow us to _try_ and submit a form based on any element you
pass it. It will try and lookup the current form and if it is
submittable it will attempt to submit it.

Instead of submitting the form directly, we try to follow the native
browser support where it looks for the first `input[type=submit]`,
`input[type=image]`, `button` or `button[type=submit]`, then it clicks
it.

This allows you to disable your submit button, or have an `onClick` that
does an `event.preventDefault()` just like the native form in a browser
would do.

* ensure we can submit a form from a closed Combobox

When the Combobox is closed, then the `Enter` keydown event will be
ignored and thus not use `event.preventDefault()`.

With recent changes where we always have an active option, it means that
you will always be able to select an option.

If we have no option at all (some edge case) or when the combobox is
closed, then the `Enter` keydown event will just bubble, allowing you to
submit a form.

Fixes: #1282

This is a continuation of a PR ([#1176](https://github.com/tailwindlabs/headlessui/pull/1176)) provided by Alexander, so wanted to include
them as a co-author because of their initial work.

Co-authored-by: Alexander Lyon <arlyon@me.com>

* ensure we can submit a form from a RadioGroup

* ensure we can submit a form from a Switch

* simplify / refactor form playground example

* update changelog

Co-authored-by: Alexander Lyon <arlyon@me.com>
2022-03-31 21:42:34 +02:00