Skip to content

Commit aa0affb

Browse files
[backport cloud/1.47] fix: render nested dropdown menus above modal overlays (#13798)
Backport of #13786 to `cloud/1.47` Automatically created by backport workflow. Co-authored-by: Dante <bunggl@naver.com>
1 parent da9cb40 commit aa0affb

3 files changed

Lines changed: 43 additions & 5 deletions

File tree

src/components/common/DropdownItem.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from 'reka-ui'
1111
import { useI18n } from 'vue-i18n'
1212
import { toValue } from 'vue'
13+
import type { StyleValue } from 'vue'
1314
1415
import { cn } from '@comfyorg/tailwind-utils'
1516
@@ -19,7 +20,12 @@ defineOptions({
1920
inheritAttrs: false
2021
})
2122
22-
defineProps<{ itemClass: string; contentClass: string; item: MenuItem }>()
23+
defineProps<{
24+
itemClass: string
25+
contentClass: string
26+
contentStyle?: StyleValue
27+
item: MenuItem
28+
}>()
2329
</script>
2430
<template>
2531
<DropdownMenuSeparator
@@ -37,6 +43,7 @@ defineProps<{ itemClass: string; contentClass: string; item: MenuItem }>()
3743
<DropdownMenuPortal>
3844
<DropdownMenuSubContent
3945
:class="contentClass"
46+
:style="contentStyle"
4047
:side-offset="2"
4148
:align-offset="-5"
4249
>
@@ -46,6 +53,7 @@ defineProps<{ itemClass: string; contentClass: string; item: MenuItem }>()
4653
:item="subitem"
4754
:item-class
4855
:content-class
56+
:content-style
4957
/>
5058
</DropdownMenuSubContent>
5159
</DropdownMenuPortal>

src/components/common/DropdownMenu.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ const contentStyle = useModalLiftedZIndex(open)
7272
:key="toValue(item.label) ?? index"
7373
:item-class
7474
:content-class
75+
:content-style
7576
:item
7677
/>
7778
</slot>

src/components/common/DropdownMenu.zindex.test.ts

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { ZIndex } from '@primeuix/utils/zindex'
2-
import { render, screen } from '@testing-library/vue'
2+
import { render, screen, within } from '@testing-library/vue'
33
import userEvent from '@testing-library/user-event'
4-
import { afterEach, describe, expect, it } from 'vitest'
4+
import type { MenuItem } from 'primevue/menuitem'
5+
import { afterEach, describe, expect, it, vi } from 'vitest'
56
import { createI18n } from 'vue-i18n'
67

78
import enMessages from '@/locales/en/main.json'
@@ -14,9 +15,9 @@ const i18n = createI18n({
1415
messages: { en: enMessages }
1516
})
1617

17-
function renderMenu() {
18+
function renderMenu(entries: MenuItem[] = [{ label: 'Item A' }]) {
1819
return render(DropdownMenu, {
19-
props: { entries: [{ label: 'Item A' }] },
20+
props: { entries },
2021
global: { plugins: [i18n], directives: { tooltip: {} } }
2122
})
2223
}
@@ -53,4 +54,32 @@ describe('DropdownMenu z-index', () => {
5354
expect(menu.style.zIndex).toBe('')
5455
expect(menu.className).toContain('z-1700')
5556
})
57+
58+
it('opens a nested menu above a registered dialog', async () => {
59+
openModal = document.createElement('div')
60+
ZIndex.set('modal', openModal, 1700)
61+
const dialogZ = Number(openModal.style.zIndex)
62+
const command = vi.fn()
63+
const user = userEvent.setup()
64+
renderMenu([
65+
{
66+
label: 'Change role',
67+
items: [
68+
{ label: 'Owner', command },
69+
{ label: 'Member', command }
70+
]
71+
}
72+
])
73+
74+
await user.click(screen.getByRole('button'))
75+
await user.click(screen.getByRole('menuitem', { name: 'Change role' }))
76+
77+
expect(await screen.findByRole('menuitem', { name: 'Owner' })).toBeVisible()
78+
expect(screen.getByRole('menuitem', { name: 'Member' })).toBeVisible()
79+
const submenu = screen
80+
.getAllByRole('menu')
81+
.find((menu) => within(menu).queryByRole('menuitem', { name: 'Owner' }))
82+
expect(submenu).toBeDefined()
83+
expect(Number(submenu?.style.zIndex)).toBeGreaterThan(dialogZ)
84+
})
5685
})

0 commit comments

Comments
 (0)