diff --git a/packages/emcn/src/components/combobox/combobox.dom.test.tsx b/packages/emcn/src/components/combobox/combobox.dom.test.tsx
index 9e999cfedb9..bd9100de247 100644
--- a/packages/emcn/src/components/combobox/combobox.dom.test.tsx
+++ b/packages/emcn/src/components/combobox/combobox.dom.test.tsx
@@ -12,8 +12,13 @@
import { act, type ReactNode, useState } from 'react'
import { createRoot, type Root } from 'react-dom/client'
import { afterEach, describe, expect, it, vi } from 'vitest'
+import { InsideModalContext } from '../modal/modal'
import { Combobox } from './combobox'
+vi.mock('next/navigation', () => ({
+ usePathname: () => '/workspace/workspace-1/home',
+}))
+
let root: Root | null = null
let container: HTMLDivElement | null = null
@@ -42,6 +47,12 @@ function click(node: HTMLElement) {
})
}
+function mouseDown(node: HTMLElement) {
+ act(() => {
+ node.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }))
+ })
+}
+
function press(node: HTMLElement, key: string) {
act(() => {
node.dispatchEvent(new KeyboardEvent('keydown', { key, bubbles: true }))
@@ -60,6 +71,7 @@ afterEach(() => {
container?.remove()
root = null
container = null
+ document.body.removeAttribute('style')
vi.restoreAllMocks()
})
@@ -72,6 +84,28 @@ describe('Combobox onOpenChange', () => {
expect(container?.querySelector('[role="listbox"]')).not.toBeNull()
})
+ it('keeps portaled options interactive inside modal content', () => {
+ const onChange = vi.fn()
+ render(
+
+
+
+ )
+
+ click(trigger())
+
+ const option = [...document.querySelectorAll('[role="option"]')].find(
+ ({ textContent }) => textContent === 'Alpha'
+ )
+ if (!option) throw new Error('Alpha option was not rendered')
+ expect(document.body.style.pointerEvents).toBe('none')
+ expect(getComputedStyle(option).pointerEvents).toBe('auto')
+
+ mouseDown(option)
+
+ expect(onChange).toHaveBeenCalledWith('alpha')
+ })
+
it('uses the overlay label for the interactive overflow layer', () => {
render(
{
click(trigger())
expect(onOpenChange).toHaveBeenCalledWith(true)
+ expect(document.body.style.pointerEvents).toBe('')
})
it('reports the close a second trigger click causes', () => {
diff --git a/packages/emcn/src/components/popover/popover.tsx b/packages/emcn/src/components/popover/popover.tsx
index 8efe883c9d4..8c549184f3b 100644
--- a/packages/emcn/src/components/popover/popover.tsx
+++ b/packages/emcn/src/components/popover/popover.tsx
@@ -56,6 +56,7 @@ import { createPortal } from 'react-dom'
import { Check, ChevronLeft, ChevronRight, Search } from '../../icons'
import { cn } from '../../lib/cn'
import { chipActiveSurfaceClass, chipHoverSurfaceClass } from '../chip/chip-chrome'
+import { InsideModalContext } from '../modal/modal'
import { TOOLTIP_MAX_WIDTH_PX, TOOLTIP_SURFACE_CLASS } from '../tooltip/tooltip-styles'
type PopoverSize = 'sm' | 'md'
@@ -209,8 +210,10 @@ const Popover: React.FC = ({
colorScheme = 'default',
open,
onOpenChange,
+ modal,
...props
}) => {
+ const insideModal = React.useContext(InsideModalContext)
const [currentFolder, setCurrentFolder] = React.useState(null)
const [folderTitle, setFolderTitle] = React.useState(null)
const [onFolderSelect, setOnFolderSelect] = React.useState<(() => void) | null>(null)
@@ -329,7 +332,12 @@ const Popover: React.FC = ({
return (
-
+
{children}