nextcloud-libraries / nextcloud-libraries/nextcloud-vue
[NcModal] Nested modal is sometimes rendered behind containg modal
Nobody has claimed this yet.
- Dominant language
- Vue
- Stars
- 246
- Forks
- 99
- Avg merge
- 1d 20h
- Merged PRs (30d)
- 103
Description
Occasionally a modal that is opened from inside another modal is not shown on top.
This happens when, in between opening the modals, other components also manipulate the children of the <body> element.
Originally observed in https://github.com/nextcloud/calendar/issues/7822
Demo
https://github.com/user-attachments/assets/df5ba71f-1d14-477a-81bc-135b30c34717
Steps to Reproduce
Given the following setup:
<template>
<div>
<NcActions
@opened="() => (popoverRemovedFromDom = false)"
@close="onPopoverClose"
>
<NcActionButton>Edit</NcActionButton>
<NcActionButton>Delete</NcActionButton>
</NcActions>
<NcButton @click="() => (outerModal = true)">Show outer modal</NcButton>
<NcModal
v-if="outerModal"
@close="() => (outerModal = false)"
name="Outer Modal"
size="large"
>
<div class="modal__content">
<div v-if="!popoverRemovedFromDom">
The button will be enabled automatically in a few seconds. We are
waiting until the popover is removed from the DOM.
</div>
<NcButton
:disabled="!popoverRemovedFromDom"
@click="() => (nestedModal = true)"
>Show nested modal</NcButton
>
<NcModal
v-if="nestedModal"
@close="() => (nestedModal = false)"
name="Nested Modal"
size="small"
>
<div class="modal__content" style="height: 256px">
Nested modal content
</div>
</NcModal>
</div>
</NcModal>
</div>
</template>
<script>
export default {
data() {
return {
outerModal: false,
nestedModal: false,
popoverRemovedFromDom: true,
};
},
methods: {
onPopoverClose() {
// We used 'close` event, because 'closed' is currently not triggered in v9.
// See https://github.com/nextcloud-libraries/nextcloud-vue/issues/8200
// Also in v8 `closed` does not guarantee that the popover was removed from the DOM.
// So in any case, we just wait a bit to guarantee that the popover was removed, which triggers the bug for modals in v8.
setTimeout(() => {
this.popoverRemovedFromDom = true;
}, 5000);
},
},
};
</script>
<style scoped>
.modal__content {
margin: 64px;
}
</style>
- Click on the menu indicator to open the popover
- Click on "Show outer modal"
- Wait until the button "Show nested modal" is enabled
- Click on "Show nested modal"
Actual Behavior
"Outer modal" is on top of "Nested Modal"
Expected Behavior
"Nested Modal" shown on top of "Outer modal".
closed event being emitted.
Environment
- Version
stable8on https://stable8--nextcloud-vue-components.netlify.app - Browser
- Chrome 144
- Firefox 147
Notes
This is caused by how the modals are appended at:
v9 uses <Teleport> from Vue 3 which does not have this issue.
Besides the popover from <NcActions> other components might also trigger this behavior if they append and remove nodes to document.body.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with src/components/NcModal/NcModal.vue at the modal append logic around line 737, then reproduce the nested-modal scenario using the supplied steps and demo. Compare the v8 behavior with Vue 3's Teleport in v9. Done means the nested modal stays above the outer modal after body children are added or removed, and the closed event is emitted.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- javascript
- Domain
- frontend
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100