Commit Graph

8 Commits

Author SHA1 Message Date
Robin Malfait e43dd5b012 assign displayName instead of name (#247)
This allows us to read a custom `displayName`, and we can default to a
`name`. React Devtools will still be able to read this information.

Fixes: #246
2021-04-02 15:55:15 +02:00
Robin Malfait 648a2843e6 Multiple new components (#220)
* add Disclosure component

* expose the Disclosure component

* add Disclosure example component page

* temporary fix selector because of JSDOM bug

* add useFocusTrap hook

* add FocusTrap component

* expose FocusTrap

* add Dialog component

* add Dialog example component page

* expose Dialog

* random cleanup

* make TypeScript a bit more happy

* add Switch.Description component for React

* add Switch.Description component for Vue

* ensure focus event is triggered on click when element is focusable

* remove Dialog.Button and Dialog.Panel from accessibility assertions

* add Portal component

* expose Portal

* always render Dialog in a Portal

* add useInertOthers hook

This will allow us to mark everything but the current ref as "inert".
This is important for screenreaders, to ensure that screenreaders and
assistive technology can't interact with other content but the current
ref.

This implementation is not ideal yet. It doesn't take into account that
you can use the hook in 2 different components. For now this is fine,
since we only use it in a Dialog and you should also probably only have
a single Dialog open at a time.

Will improve this in the future!

* use the useInertOthers hook

* add scroll lock to the dialog

* ensure we respect autoFocus on form elements within the Dialog

If we have an autoFocus on an input, that input will receive focus. Once
we try to focus the first focusable element in the Dialog this could be
lead to unwanted behaviour. Therefore we check if the focus already is
within the Dialog, if it is, keep it like that.

* only mark aria-modal when Dialog is open

* add initialFocus option to Dialog, FocusTrap & useFocusTrap

* add tests and a few fixes for the initialFocusRef functionality

* forward ref to underlying Dialog component

* close Dialog when it becomes hidden

Could happen when this is in md:hidden for example

* prevent infinite loop

When we `Tab` in a FocusTrap it will try and focus the Next element. If
we are in a state where none of the elements inside the FocusTrap can be
focused, then we keep trying to focus the next one in line. This results
in an infinite loop...

To mitigate this issue, we check if we looped around, if we did, it
means that we tried all the other focusable elements, therefore we can
stop.

* isIntersecting doesn't work in every scenario

When page is scrollable, when dialog is translated of the page. Now just checking for sizes, which should be enough for md:hiden cases

* render Portal contents in a div

Otherwise you can't use multiple Portal components if you render multiple children inside each Portal

* ensure the props bag is typed

* add getByText and assertContainsActiveElement helpers

* add Popover component

* expose Popover

* add Popover example component page

* add quick checks to prevent useless renders

* drop incorrect close function

* update Changelog

* make test error more readable when comparing DOM nodes

* actually call .focus() on the element

This ensures that the document.activeElement becomes the focused element.

* improve useSyncRefs, because ...refs is *always* different

* add dedicated focus management utilities

* refactor useFocusTrap, use focus management utilities

* fix regression while using outside click

There might be a chance that you didn't even notice this *bug*. The idea
is that when you click outside, that the Menu or Listbox closes. However
there is another step that happens:

1. When you click on a focusable item, keep the focus on that item.
2. When you click on a non-focusable item, move focus back to the
   Menu.Button or Listbox.Button

We broke part 2, we never returned to the Menu.Button or Listbox.Button.
This is (might) be important for screenreaders so that they don't "get lost",
because if you click on a non-focusable item, the document.body becomes
the active element. Confusing.

* add outside-click to Dialog itself

* update docs
2021-04-02 15:55:14 +02:00
Robin Malfait bb68793f08 correctly handle TypeScript render abstractions 2021-02-11 19:19:12 +01: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 6e3d496998 feat: add Switch component (#26)
* add Switch component

* add tests to verify that we can click the label to toggle the Switch

* use onKeyUp to prevent triggering the onClick in firefox
2020-10-05 16:47:31 +02:00
Robin Malfait 2747745891 ensure that you can not click disabled items 2020-09-25 16:09:40 +02:00
Robin Malfait 76c24680c5 make render logic consistent with the Vue implementation 2020-09-16 23:44:38 +02:00