From 7049b2e7a2ffd2795b0d9a509a8a2327bea8e8ec Mon Sep 17 00:00:00 2001 From: Robin Malfait Date: Mon, 22 Mar 2021 14:53:18 +0100 Subject: [PATCH] WIP --- .../src/components/popover/popover.test.tsx | 2 +- .../src/components/focus-trap/focus-trap.vue | 27 +++++++++++ .../@headlessui-vue/examples/src/routes.json | 11 +++++ .../components/focus-trap/focus-trap.test.ts | 4 +- .../src/components/focus-trap/focus-trap.ts | 5 ++ .../src/components/popover/popover.test.ts | 8 +++- .../src/components/popover/popover.ts | 48 ++++++++++++++----- 7 files changed, 89 insertions(+), 16 deletions(-) create mode 100644 packages/@headlessui-vue/examples/src/components/focus-trap/focus-trap.vue diff --git a/packages/@headlessui-react/src/components/popover/popover.test.tsx b/packages/@headlessui-react/src/components/popover/popover.test.tsx index 4d62967a07..3cc6877f76 100644 --- a/packages/@headlessui-react/src/components/popover/popover.test.tsx +++ b/packages/@headlessui-react/src/components/popover/popover.test.tsx @@ -774,7 +774,7 @@ describe('Keyboard interactions', () => { }) ) - it( + it.only( 'should close the Popover menu once we Tab out of the Popover.Group', suppressConsoleLogs(async () => { render( diff --git a/packages/@headlessui-vue/examples/src/components/focus-trap/focus-trap.vue b/packages/@headlessui-vue/examples/src/components/focus-trap/focus-trap.vue new file mode 100644 index 0000000000..ebc7a907d2 --- /dev/null +++ b/packages/@headlessui-vue/examples/src/components/focus-trap/focus-trap.vue @@ -0,0 +1,27 @@ + + + diff --git a/packages/@headlessui-vue/examples/src/routes.json b/packages/@headlessui-vue/examples/src/routes.json index 4fe95c057d..631b59a22b 100644 --- a/packages/@headlessui-vue/examples/src/routes.json +++ b/packages/@headlessui-vue/examples/src/routes.json @@ -60,5 +60,16 @@ "component": "./components/switch/switch.vue" } ] + }, + { + "name": "Focus Trap", + "path": "/focus-trap", + "children": [ + { + "name": "FocusTrap (basic)", + "path": "/focus-trap/focus-trap", + "component": "./components/focus-trap/focus-trap.vue" + } + ] } ] diff --git a/packages/@headlessui-vue/src/components/focus-trap/focus-trap.test.ts b/packages/@headlessui-vue/src/components/focus-trap/focus-trap.test.ts index b22bd54346..5cdca8adce 100644 --- a/packages/@headlessui-vue/src/components/focus-trap/focus-trap.test.ts +++ b/packages/@headlessui-vue/src/components/focus-trap/focus-trap.test.ts @@ -32,7 +32,7 @@ function renderTemplate(input: string | Partial { - renderTemplate( + let { debug } = renderTemplate( ` @@ -40,6 +40,8 @@ it.only('should focus the first focusable element inside the FocusTrap', () => { ` ) + debug() + console.log('In test:', document.activeElement?.outerHTML) assertActiveElement(getByText('Trigger')) diff --git a/packages/@headlessui-vue/src/components/focus-trap/focus-trap.ts b/packages/@headlessui-vue/src/components/focus-trap/focus-trap.ts index c00c1ab564..3cbe166a16 100644 --- a/packages/@headlessui-vue/src/components/focus-trap/focus-trap.ts +++ b/packages/@headlessui-vue/src/components/focus-trap/focus-trap.ts @@ -6,6 +6,7 @@ import { // Types PropType, Ref, + onUnmounted, } from 'vue' import { render } from '../../utils/render' import { useFocusTrap } from '../../hooks/use-focus-trap' @@ -43,6 +44,10 @@ export let FocusTrap = defineComponent({ console.log('After:', document.activeElement?.outerHTML) }) + onUnmounted(() => { + console.log('Unmount:', document.activeElement?.outerHTML) + }) + return { el: container } }, }) diff --git a/packages/@headlessui-vue/src/components/popover/popover.test.ts b/packages/@headlessui-vue/src/components/popover/popover.test.ts index d7e57582cc..b7341dcc66 100644 --- a/packages/@headlessui-vue/src/components/popover/popover.test.ts +++ b/packages/@headlessui-vue/src/components/popover/popover.test.ts @@ -896,6 +896,7 @@ describe('Keyboard interactions', () => { // Verify the next link is now focused assertActiveElement(getByText('Next')) + await new Promise(nextTick) // Verify the popover is closed assertPopoverButton({ state: PopoverState.InvisibleUnmounted }) @@ -1216,10 +1217,10 @@ describe('Keyboard interactions', () => { }) ) - it( + it.only( 'should focus the PopoverButton when pressing Shift+Tab when we focus inside the PopoverPanel', suppressConsoleLogs(async () => { - renderTemplate( + let { debug } = renderTemplate( ` Trigger 1 @@ -1242,6 +1243,9 @@ describe('Keyboard interactions', () => { // Tab out of the Panel await press(shift(Keys.Tab)) + console.log(document.activeElement?.outerHTML) + + debug() // Ensure the PopoverButton is focused again assertActiveElement(getPopoverButton()) diff --git a/packages/@headlessui-vue/src/components/popover/popover.ts b/packages/@headlessui-vue/src/components/popover/popover.ts index 88041fc66c..4aaf655dbd 100644 --- a/packages/@headlessui-vue/src/components/popover/popover.ts +++ b/packages/@headlessui-vue/src/components/popover/popover.ts @@ -1,6 +1,9 @@ import { defineComponent, inject, + onMounted, + onUnmounted, + onBeforeUnmount, provide, ref, watchEffect, @@ -135,14 +138,18 @@ export let Popover = defineComponent({ watchEffect(() => registerPopover?.(registerBag)) // Handle focus out - useWindowEvent('focus', () => { - if (popoverState.value !== PopoverStates.Open) return - if (isFocusWithinPopoverGroup()) return - if (!button) return - if (!panel) return + useWindowEvent( + 'focus', + () => { + if (popoverState.value !== PopoverStates.Open) return + if (isFocusWithinPopoverGroup()) return + if (!button) return + if (!panel) return - api.closePopover() - }) + api.closePopover() + }, + true + ) // Handle outside click useWindowEvent('mousedown', (event: MouseEvent) => { @@ -222,6 +229,7 @@ export let PopoverButton = defineComponent({ useWindowEvent( 'focus', () => { + console.log('Ding dong', document.activeElement?.outerHTML) previousActiveElementRef.value = activeElementRef.value activeElementRef.value = document.activeElement }, @@ -390,7 +398,7 @@ export let PopoverPanel = defineComponent({ let propsWeControl = { ref: 'el', id: this.id, - onKeyDown: this.handleKeyDown, + onKeydown: this.handleKeyDown, } return render({ @@ -408,11 +416,15 @@ export let PopoverPanel = defineComponent({ provide(PopoverPanelContext, api.panelId) + onUnmounted(() => { + api.panel.value = null + }) + // Move focus within panel watchEffect(() => { if (!focus) return if (api.popoverState.value !== PopoverStates.Open) return - if (!dom(api.panel)) return + if (!api.panel) return let activeElement = document.activeElement as HTMLElement if (dom(api.panel)?.contains(activeElement)) return // Already focused within Dialog @@ -438,7 +450,9 @@ export let PopoverPanel = defineComponent({ let result = focusIn(dom(api.panel)!, event.shiftKey ? Focus.Previous : Focus.Next) if (result === FocusResult.Underflow) { - return dom(api.button)?.focus() + dom(api.button)?.focus() + console.log(document.activeElement?.outerHTML) + return } else if (result === FocusResult.Overflow) { if (!dom(api.button)) return @@ -486,7 +500,17 @@ export let PopoverGroup = defineComponent({ props: { as: { type: [Object, String], default: 'div' }, }, - setup(props, { slots, attrs }) { + render() { + let propsWeControl = { ref: 'el' } + + return render({ + props: { ...this.$props, ...propsWeControl }, + slot: {}, + attrs: this.$attrs, + slots: this.$slots, + }) + }, + setup() { let groupRef = ref(null) let popovers = ref([]) @@ -529,6 +553,6 @@ export let PopoverGroup = defineComponent({ closeOthers, }) - return () => render({ props, slot: {}, attrs, slots }) + return { el: groupRef } }, })