Resolve initialFocusRef correctly (#1276)
* resolve initialFocusRef correctly If you are passing a Ref to a component, you don't get the underlying DOM node even if you put it on the element manually. The ref will be a ref to the _component_. This means that the initialFocusRef can be a DOM element or a Vue component instance. Resolving it guarantees us to resolve to an HTMLElement or null but not a component. * update changelog
This commit is contained in:
@@ -65,6 +65,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
- Fix Tree-shaking support ([#1247](https://github.com/tailwindlabs/headlessui/pull/1247))
|
||||
- Stop propagation on the Popover Button ([#1263](https://github.com/tailwindlabs/headlessui/pull/1263))
|
||||
- Fix incorrect closing while interacting with third party libraries in `Dialog` component ([#1268](https://github.com/tailwindlabs/headlessui/pull/1268))
|
||||
- Resolve `initialFocusRef` correctly ([#1276](https://github.com/tailwindlabs/headlessui/pull/1276))
|
||||
|
||||
### Added
|
||||
|
||||
|
||||
@@ -698,6 +698,76 @@ describe('Keyboard interactions', () => {
|
||||
assertActiveElement(document.getElementById('a'))
|
||||
})
|
||||
)
|
||||
|
||||
it(
|
||||
'should be possible to tab around when using the initialFocus ref on a component',
|
||||
suppressConsoleLogs(async () => {
|
||||
let CustomComponent = defineComponent({
|
||||
name: 'CustomComponent',
|
||||
setup() {
|
||||
return () => h('input')
|
||||
},
|
||||
})
|
||||
|
||||
renderTemplate({
|
||||
components: {
|
||||
CustomComponent,
|
||||
},
|
||||
template: `
|
||||
<div>
|
||||
<button id="trigger" @click="toggleOpen">
|
||||
Trigger
|
||||
</button>
|
||||
<Dialog :open="isOpen" @close="setIsOpen" :initialFocus="initialFocusRef">
|
||||
Contents
|
||||
<TabSentinel id="a" />
|
||||
<CustomComponent type="text" id="b" ref="initialFocusRef" />
|
||||
</Dialog>
|
||||
</div>
|
||||
`,
|
||||
setup() {
|
||||
let isOpen = ref(false)
|
||||
let initialFocusRef = ref(null)
|
||||
return {
|
||||
isOpen,
|
||||
initialFocusRef,
|
||||
setIsOpen(value: boolean) {
|
||||
isOpen.value = value
|
||||
},
|
||||
toggleOpen() {
|
||||
isOpen.value = !isOpen.value
|
||||
},
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
assertDialog({ state: DialogState.InvisibleUnmounted })
|
||||
|
||||
// Open dialog
|
||||
await click(document.getElementById('trigger'))
|
||||
|
||||
// Verify it is open
|
||||
assertDialog({
|
||||
state: DialogState.Visible,
|
||||
attributes: { id: 'headlessui-dialog-1' },
|
||||
})
|
||||
|
||||
// Verify that the input field is focused
|
||||
assertActiveElement(document.getElementById('b'))
|
||||
|
||||
// Verify that we can tab around
|
||||
await press(Keys.Tab)
|
||||
assertActiveElement(document.getElementById('a'))
|
||||
|
||||
// Verify that we can tab around
|
||||
await press(Keys.Tab)
|
||||
assertActiveElement(document.getElementById('b'))
|
||||
|
||||
// Verify that we can tab around
|
||||
await press(Keys.Tab)
|
||||
assertActiveElement(document.getElementById('a'))
|
||||
})
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import { Keys } from '../keyboard'
|
||||
import { focusElement, focusIn, Focus, FocusResult } from '../utils/focus-management'
|
||||
import { getOwnerDocument } from '../utils/owner'
|
||||
import { useEventListener } from './use-event-listener'
|
||||
import { dom } from '../utils/dom'
|
||||
|
||||
export enum Features {
|
||||
/** No features enabled for the `useFocusTrap` hook. */
|
||||
@@ -96,10 +97,12 @@ export function useFocusTrap(
|
||||
let containerElement = container.value
|
||||
if (!containerElement) return
|
||||
|
||||
let initialFocusElement = dom(options.value.initialFocus)
|
||||
|
||||
let activeElement = ownerDocument.value?.activeElement as HTMLElement
|
||||
|
||||
if (options.value.initialFocus?.value) {
|
||||
if (options.value.initialFocus?.value === activeElement) {
|
||||
if (initialFocusElement) {
|
||||
if (initialFocusElement === activeElement) {
|
||||
previousActiveElement.value = activeElement
|
||||
return // Initial focus ref is already the active element
|
||||
}
|
||||
@@ -109,8 +112,8 @@ export function useFocusTrap(
|
||||
}
|
||||
|
||||
// Try to focus the initialFocus ref
|
||||
if (options.value.initialFocus?.value) {
|
||||
focusElement(options.value.initialFocus.value)
|
||||
if (initialFocusElement) {
|
||||
focusElement(initialFocusElement)
|
||||
} else {
|
||||
if (focusIn(containerElement, Focus.First) === FocusResult.Error) {
|
||||
console.warn('There are no focusable elements inside the <FocusTrap />')
|
||||
|
||||
Reference in New Issue
Block a user