Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dialog component's portal root element gets lost when navigating between routes in a Vue 3 app #3116

Closed
DaRosenberg opened this issue Apr 20, 2024 · 8 comments
Assignees

Comments

@DaRosenberg
Copy link

DaRosenberg commented Apr 20, 2024

What package within Headless UI are you using?

@headlessui/vue

What version of that package are you using?

1.7.20

What browser are you using?

Chrome and Safari (reproduces in both).

Reproduction URL

I've boiled this down to a bare minimum repro.

Describe your issue

When using vue-router it seems the Dialog component only works in components that are loaded on the initial route (i.e. on page load). When navigating to a different route, thus loading different components, instances of Dialog inside those newly visible components fail to open and the dialog component logs a console warning:

There are no focusable elements inside the <FocusTrap />

While troubleshooting I've noticed that this coincides with the headlessui-portal-root element getting lost on navigation between routes (i.e. removed from the DOM and then not restored when the new route is loaded).

The repro I put together illustrates this.

To reproduce the issue, use the links to navigate between the first and second view.

Notice how the headlessui-portal-root element is present in the DOM on the view of the initial page load, and dialog opens fine on this view. When navigating to the other view, the headlessui-portal-root element disappears, the dialog does not open.

When navigating back to the initial route, the headlessui-portal-root element is restored and the dialog once again opens fine on the first view.

Note that the exact same TestDialog component is used on both views, and the issue has nothing to do with missing focusable elements in the dialog itself.

@sk31park
Copy link

I have a same problem..

@Danita
Copy link

Danita commented Apr 24, 2024

Oh god, I've been battling the last 2 hours with this thinking it was a problem on my application. It happens here too. I haven't found a workaround yet.

@Danita
Copy link

Danita commented Apr 24, 2024

I found that using v-if to show and hide the dialog forces it to recreate the portal and work around the error for now. You lose any transitions of course.

@DaRosenberg
Copy link
Author

@Danita I would kill for a workaround, can you show yours? What are you using as the condition in your v-if?

@Danita
Copy link

Danita commented Apr 24, 2024

@DaRosenberg It's nothing fancy, only a matter of conditionally drawing the element on the page that uses it, not only setting its open prop like it says on the documentation. For example, I was creating a reusable custom component for my app that looks like this:

<template>
	<Dialog :open="show" @close="$emit('cancel')">
	...
	</Dialog>
</template>
<script setup>
	import { Dialog, DialogPanel, DialogTitle } from '@headlessui/vue';
	defineProps({
		show: { type: Boolean, default: false },
		title: { type: String, default: 'Dialog title' },
	})
	const emit = defineEmits(["cancel", "save"]);
</script>

So when I use that component in my app, to show the modal I should set the show prop to true. My workaround was to also insert the modal conditionally with v-if.

<template>
	...
	<button @click="showModal=true">Edit</button>
	...
	<modal v-if="showModal" :show="showModal" @cancel="showModal=false" @save="handleSave">
		...
	</modal>
</template>
<script setup>
	import Modal from "../../components/Modal.vue";
	import { ref } from 'vue';
	const showModal = ref(false);
</script>

Hope it helps.

@DaRosenberg
Copy link
Author

@Danita that works like a charm, thank you! 🙏🏻

@thecrypticace
Copy link
Contributor

thecrypticace commented Apr 26, 2024

Sorry about that! One of our changes cause a timing issue when switching dialogs in the same "tick". Fixed in @headlessui/vue v1.7.21

npm install @headlessui/vue@latest

@Danita
Copy link

Danita commented May 6, 2024

@thecrypticace thank you!! <3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants