ecdebf3d59ca1bb3641c82772a4e46e792974d06
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
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> |
||
|
|
3e19aa5c97 |
Properly merge incoming props (#1265)
* rename inconsistent `passThroughProps` and `passthroughProps` to more
concise `incomingProps`
This is going to make a bit more sense in the next commits of this
branch, hold on!
* split props into `propsWeControl` and `propsTheyControl`
This will allow us to merge the props with a bit more control. Instead
of overriding every prop from the user' props with our props, we can now
merge event listeners.
* update `render` API to accept `propsWeControl` and `propsTheyControl`
* improve the merge logic
This will essentially do the exact same thing we were doing before:
```js
let props = { ...propsTheyControl, ...propsWeControl }
```
But instead of overriding everything, we will merge the event listener
related props like `onClick`, `onKeyDown`, ...
* fix typo in tests
* simplify naming
- Rename `propsWeControl` to `ourProps`
- Rename `propsTheyControl` to `theirProps`
* update changelog
|
||
|
|
273719cb5d |
Ensure focus trap, Tabs and Dialog play well together (#1231)
* add internal FocusSentinel component
This component will allow you to catch the focus and forward it to a new
element. The catch is that it will retry to do that because sometimes
components won't be available yet.
E.g.: We want to focus the first Tab component if it is rendered inside
the Dialog. However, a Tab will register itself in the next tick,
triggering a re-render and only then will it be `selected`. This is a
bit too late for the FocusTrap component.
The FocusSentinel should fix this by catching the focus, and forwarding
it to the correct component. Once that is done, it will remove itself
from the DOM tree so that you can't ever focus that element anymore.
This should fix potential `<tab>` and `<shift+tab>` behaviour.
* find the selectedIndex asap
* use the FocusSentinel and forward it to the correct Tab
* add example Tab in Dialog example
* suppress console warnings
Because we are firing `setState` calls within the component, React is
yelling at us for not using `act(() => { ... })`. Welp, not going to add
those calls inside the component just for tests...
* update changelog
|
||
|
|
7bb89871ba |
Add <form> compatibility (#1214)
* implement `objetToFormEntries` functionality
If we are working with more complex data structures then we have to
encode those data structures into a syntax that the HTML can understand.
This means that we have to use `<input type="hidden" name="..." value="...">` syntax.
To convert a simple array we can use the following syntax:
```js
// Assuming we have a `name` of `person`
let input = ['Alice', 'Bob', 'Charlie']
```
Results in:
```html
<input type="hidden" name="person[]" value="Alice" />
<input type="hidden" name="person[]" value="Bob" />
<input type="hidden" name="person[]" value="Charlie" />
```
Note: the additional `[]` in the name attribute.
---
A more complex object (even deeply nested) can be encoded like this:
```js
// Assuming we have a `name` of `person`
let input = {
id: 1,
name: {
first: 'Jane',
last: 'Doe'
}
}
```
Results in:
```html
<input type="hidden" name="person[id]" value="1" />
<input type="hidden" name="person[name][first]" value="Jane" />
<input type="hidden" name="person[name][last]" value="Doe" />
```
* implement VisuallyHidden component
* implement and export some extra helper utilities
* implement form element for Switch
* implement form element for Combobox
* implement form element for RadioGroup
* implement form element for Listbox
* add combined forms example to the playground
* update changelog
* enable support for iterators
* ensure to compile dom iterables
* remove unused imports
|
||
|
|
084a2497d8 |
Fix incorrect nested Dialogs behaviour (#489)
* add tests to verify the nested Dialog behaviour * set mounted to true once rendered once * cache useWindowEvent listener We only care about the very last version of the listener function. This allows us to only change the event listener if the event name (string) and options (boolean | object) change. * add/delete messages when mounting/unmounting We don't require a dedicated hook anymore, so this is a bit of cleanup! * add comments to the FocusResult enum * splitup functionality and make it a bit more clear using feature flags * add getDialogOverlays helper * simplify the Portal component We don't need to add the current element to the Stack. We only want to take care of that in the Dialog component itself. * drop dom-containers Currently it is only used in a single spot, so I inlined it into that file. * simplify the FocusTrap component, use new API * improve Dialog component * update CHANGELOG |
||
|
|
34a10538d6 |
Open closed state (#466)
* simplify examples by using the implicit open/closed state * introduce Open/Closed context (React) * use Open/Closed context in Dialog component (React) * use Open/Closed context in Disclosure component (React) * use Open/Closed context in Listbox component (React) * use Open/Closed context in Menu component (React) * use Open/Closed context in Popover component (React) * use Open/Closed context in Transition component (React) * introduce Open/Closed context (Vue) * use Open/Closed context in Dialog component (Vue) * use Open/Closed context in Disclosure component (Vue) * use Open/Closed context in Listbox component (Vue) * use Open/Closed context in Menu component (Vue) * use Open/Closed context in Popover component (Vue) * use Open/Closed context in Transition component (Vue) * use a ref in the Description comopnent This allows us to update the ref and everything should work after that. Currently we only saw the "current" state. * add more Vue examples * update changelog |
||
|
|
6f8225c437 |
Next batch of Vue components (#282)
* add little editor hack By adding a html`..` to the template strings editors can get syntax highlighting for these template strings. Even better, prettier can even format the contents inside those because now it is "aware" of what kind of content is inside of these template strings. You might notice that for the Menu component I have a jsx`..`, this is another little hack, this only provides us with syntax highlighting and not with prettier support. The reason why we have this is that for some reason, when you have: html` <MenuItem> ` It will be formatted as: html` <menuitem> ` There might exist a better name we can use instead of jsx, but for now, this will do. Having syntax highlighting is already 10x better than what we had before! * add Alert component * update changelog * update REACT readme for Alert component * expose Alert component * add Disclosure component * expose Disclosure component * add FocusTrap component * add FocusTrap example * expose FocusTrap * update test utils We've been making some changes in the React utils, so we have to update them here as well! * add Popover component * expose Popover * drop unused state * type Disclosure's API object * add Portal component * add Portal example * expose Portal component * use correct containElement assertion * add useInertOthers hook * add Dialog component * fix various typo's * expose Dialog * add Popover example * force focus on the Popover button on click * drop own id when using labels We are nesting the Label and Description components, if we also add the id of ourselves we get strange results when using Voice Over. First you would hear the contents (which includes the labels and descriptions) then you would hear the labels and descriptions again. We don't want to hear things twice! * add Dialog example * ensure to stop propagation Otherwise if you nest a Menu inside a Dialog and you press `Escape` the Dialog will close as well, which is not the expected behaviour. * improve focus management When you trigger a Popover using a `click` event, then start using `Tab` the next `a`-tags do not contain the default focus styles. These only happen when you trigger it using the keyboard first. Using a tabindex="0" does make it "focusable" and the default browser styles will be visible. If we remove the tabindex in a requestAnimationFrame or a setTimeout then the focus styles will be removed as well. This should not cause to many issues (fingers crossed) because the document.activeElement was already referring to the correct element! * remove Alert component There are a lot of unknowns and context dependendant questions to implement Alerts in a good way. The current Alert component just had a role set, and it had no JS attached. We will revisit this, once we start working on Alert Dialogs, Notification center notifications (dismissable, hide after x time, ...) * ensure Popover.Overlay auto shows/hides based on Popover state * enable focus trapping based on `open` prop Only enable focus trapping in the Dialog when `open` is true, regardless of the `static` prop. * handle attrs on Dialog manually * add low level Description component * add low level Label component * add RadioGroup component * expose RadioGroup * update README with links to new components * update changelog with all the changes * add RadioGroup example * improve type in test * cleanup internal Dialog Description We have a low level Description component abstraction that can be used instead of the Dialog specifiction Description. * refactor raw window events to a shared useWindowEvent * passthrough prop bag via context for abstract Description The Description component is a generic low level component that is re-used. This causes an issue that the render prop "bag"/"slot" doesn't contain the data from let's say a Dialog component. This commit will ensure that you can specify a bag (React) and slot (Vue) on the DescriptionProvider, so that the Description component can read it from the context. * improve render function in React These contain a few changes that are purely internal changes. Nothing changes / breaks in the public API of the components. - Instead of using multiple arguments in your `render()` functions, we now use an object. - `propsBag` / `bag` is renamed to `slot`. - We also provide a `name` to the render function, so that we can use that to improve error messages. * use the new internal render api (React) * improve render function in Vue * use the new internal render api (Vue) |
||
|
|
00cc8c50e3 |
Add Alert & RadioGroup components (#274)
* add Alert component * expose Alert * rename forgotten FLYOUT to POPOVER * use PopoverRenderPropArg * organize imports in a consistent way * ensure Portals behave as expected Portals can be nested from a React perspective, however in the DOM they are rendered as siblings, this is mostly fine. However, when they are rendered inside a Dialog, the Dialog itself is marked with `role="modal"` which makes all the other content inert. This means that rendering Menu.Items in a Portal or an Alert in a portal makes it non-interactable. Alerts are not even announced. To fix this, we ensure that we make the `root` of the Portal the actual dialog. This allows you to still interact with it, because an open modal is the "root" for the assistive technology. But there is a catch, a Dialog in a Dialog *can* render as a sibling, because you force the focus into the new Dialog. So we also ensured that Dialogs are always rendered in the portal root, and not inside another Dialog. * add dialog with alert example * add internal Description component * add internal Label component * add RadioGroup component * expose RadioGroup * add RadioGroup example * ensure to include tha RadioGroup.Option own id * update changelog * split documentation |
||
|
|
958e3ea8c6 |
bug fixes (#261)
* apply re-focus bug fix to Popover * force focus in Menu.Items from within Menu.Items component itself * force focus in Listbox.Options from within Listbox.Options component itself * fix undefined values in id's We were setting the element in state, but updates to the id were not taken into account * update the caniuse db * ensure useInertOthers works in multiple places Previously each hook call would take care of the whole tree. However when multiple calls to this hook are happening we need to make sure that you are not removing the aria-hidden when another hook is still used. This will fix that by keeping track of a list of "interactable" items, and updating the parents (root of the body) accordingly. * add the concept of a Stack When you are rendering a Dialog, we will make sure that this Dialog is rendered inside a Portal. However, when you are also rendering a Menu, there is a chance that your Menu doesn't fit within the Dialog, therefore you will likely render the Menu.Items inside a Portal so that you can style it as if it is rendered inside but overflows the Dialog correctly. This introduces an interesting/annoying problem. Your Menu.Items are now rendered in a Portal, as a *sibling* to the Dialog. This means that autoFocus, focusTrap, ... all these features don't work as expected. Introducing this Stack will allow us to register DOM nodes into a list of contains that we consider being part of the main container. In other words, the sibling Menu.Items will now be considered part of the Dialog. Even though it is rendered *outside* of the Dialog. This concept also allows for some fun stuff, for example, nesting Dialog's is no problem with this approach. Dialogs are technically rendered as siblings in the Portal, but the FocusTrap, and all that just works as expected. * capture keyboard events in the capturing phase This will allow us to use event.stopPropagation() in the code (which will be required, probably) but still see the keystrokes in the playground. * stop propagating keyboard events This looks a bit silly, and ideally we can solve this in a more elegant way. However when you nest a Menu inside a Dialog, both of those components have a `close on escape` functionality built in. However when your Menu is open, and you press escape, you only want to close the Menu, not the Dialog. Therefore if we `event.stopPropagation()` it allows us to stop the `escape` keystroke in the Menu from reaching all the way to the Dialog itself. * update Dialog example that showcases nested Dialogs, and nested Menu * update changelog |