nextcloud-libraries / nextcloud-libraries/nextcloud-vue

[NcModal] Nested modal is sometimes rendered behind containg modal

Open
#8,201 0 comments 0 reactions 0 assignees View on GitHub

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>
  1. Click on the menu indicator to open the popover
  2. Click on "Show outer modal"
  3. Wait until the button "Show nested modal" is enabled
  4. 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

Notes

This is caused by how the modals are appended at:

https://github.com/nextcloud-libraries/nextcloud-vue/blob/b0adf68083d85a9e751c82ca7bc81e4d75a79675/src/components/NcModal/NcModal.vue#L737

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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.