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
This commit is contained in:
Robin Malfait
2022-08-22 17:00:15 +02:00
committed by GitHub
parent 486ac8075d
commit b301f04c77
6 changed files with 34 additions and 4 deletions
+1
View File
@@ -30,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Ensure `Disclosure.Panel` is properly linked ([#1747](https://github.com/tailwindlabs/headlessui/pull/1747))
- Only select the active option when using "singular" mode when pressing `<tab>` in the `Combobox` component ([#1750](https://github.com/tailwindlabs/headlessui/pull/1750))
- Improve the types of the `Combobox` component ([#1761](https://github.com/tailwindlabs/headlessui/pull/1761))
- Only restore focus to the `Menu.Button` if necessary when activating a `Menu.Option` ([#1782](https://github.com/tailwindlabs/headlessui/pull/1782))
## Changed
@@ -35,6 +35,7 @@ import {
sortByDomNode,
Focus as FocusManagementFocus,
focusFrom,
restoreFocusIfNecessary,
} from '../../utils/focus-management'
import { useOutsideClick } from '../../hooks/use-outside-click'
import { useTreeWalker } from '../../hooks/use-tree-walker'
@@ -463,7 +464,7 @@ let Items = forwardRefWithAs(function Items<TTag extends ElementType = typeof DE
let { dataRef } = state.items[state.activeItemIndex]
dataRef.current?.domRef.current?.click()
}
disposables().nextFrame(() => state.buttonRef.current?.focus({ preventScroll: true }))
restoreFocusIfNecessary(state.buttonRef.current)
break
case Keys.ArrowDown:
@@ -615,7 +616,7 @@ let Item = forwardRefWithAs(function Item<TTag extends ElementType = typeof DEFA
let handleClick = useEvent((event: MouseEvent) => {
if (disabled) return event.preventDefault()
dispatch({ type: ActionTypes.CloseMenu })
disposables().nextFrame(() => state.buttonRef.current?.focus({ preventScroll: true }))
restoreFocusIfNecessary(state.buttonRef.current)
})
let handleFocus = useEvent(() => {
@@ -1,3 +1,4 @@
import { disposables } from './disposables'
import { match } from './match'
import { getOwnerDocument } from './owner'
@@ -99,6 +100,18 @@ export function isFocusableElement(
})
}
export function restoreFocusIfNecessary(element: HTMLElement | null) {
let ownerDocument = getOwnerDocument(element)
disposables().nextFrame(() => {
if (
ownerDocument &&
!isFocusableElement(ownerDocument.activeElement as HTMLElement, FocusableMode.Strict)
) {
focusElement(element)
}
})
}
export function focusElement(element: HTMLElement | null) {
element?.focus({ preventScroll: true })
}
+1
View File
@@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Make form components uncontrollable ([#1683](https://github.com/tailwindlabs/headlessui/pull/1683))
- Improve `Combobox` re-opening keyboard issue on mobile ([#1732](https://github.com/tailwindlabs/headlessui/pull/1732))
- Only select the active option when using "singular" mode when pressing `<tab>` in the `Combobox` component ([#1750](https://github.com/tailwindlabs/headlessui/pull/1750))
- Only restore focus to the `MenuButton` if necessary when activating a `MenuOption` ([#1782](https://github.com/tailwindlabs/headlessui/pull/1782))
## [1.6.7] - 2022-07-12
@@ -28,6 +28,7 @@ import {
sortByDomNode,
Focus as FocusManagementFocus,
focusFrom,
restoreFocusIfNecessary,
} from '../../utils/focus-management'
import { useOutsideClick } from '../../hooks/use-outside-click'
@@ -384,7 +385,7 @@ export let MenuItems = defineComponent({
dom(_activeItem.dataRef.domRef)?.click()
}
api.closeMenu()
nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true }))
restoreFocusIfNecessary(dom(api.buttonRef))
break
case Keys.ArrowDown:
@@ -531,7 +532,7 @@ export let MenuItem = defineComponent({
function handleClick(event: MouseEvent) {
if (props.disabled) return event.preventDefault()
api.closeMenu()
nextTick(() => dom(api.buttonRef)?.focus({ preventScroll: true }))
restoreFocusIfNecessary(dom(api.buttonRef))
}
function handleFocus() {
@@ -1,3 +1,4 @@
import { nextTick } from 'vue'
import { match } from './match'
import { getOwnerDocument } from './owner'
@@ -92,6 +93,18 @@ export function isFocusableElement(
})
}
export function restoreFocusIfNecessary(element: HTMLElement | null) {
let ownerDocument = getOwnerDocument(element)
nextTick(() => {
if (
ownerDocument &&
!isFocusableElement(ownerDocument.activeElement as HTMLElement, FocusableMode.Strict)
) {
focusElement(element)
}
})
}
export function focusElement(element: HTMLElement | null) {
element?.focus({ preventScroll: true })
}