diff --git a/.changeset/solid-floating-panel-lazy-mount.md b/.changeset/solid-floating-panel-lazy-mount.md
new file mode 100644
index 0000000000..58a3dd8efe
--- /dev/null
+++ b/.changeset/solid-floating-panel-lazy-mount.md
@@ -0,0 +1,7 @@
+---
+'@ark-ui/solid': patch
+---
+
+Fixed FloatingPanel `Content` and `Positioner` not reacting to presence changes. The panel never appeared when
+`lazyMount` was used, and was never removed from the DOM when `unmountOnExit` was used. `Content` now also forwards the
+presence ref so exit animations are tracked before unmounting.
diff --git a/packages/solid/src/components/floating-panel/floating-panel-content.tsx b/packages/solid/src/components/floating-panel/floating-panel-content.tsx
index 730397e0a8..c698f0696b 100644
--- a/packages/solid/src/components/floating-panel/floating-panel-content.tsx
+++ b/packages/solid/src/components/floating-panel/floating-panel-content.tsx
@@ -1,4 +1,6 @@
import { mergeProps } from '@zag-js/solid'
+import { Show } from 'solid-js'
+import { composeRefs } from '../../utils/compose-refs.ts'
import { type HTMLProps, type PolymorphicProps, ark } from '../factory.tsx'
import { usePresenceContext } from '../presence/index.tsx'
import { useFloatingPanelContext } from './use-floating-panel-context.ts'
@@ -15,9 +17,9 @@ export const FloatingPanelContent = (props: FloatingPanelContentProps) => {
props,
)
- if (presence().unmounted) {
- return null
- }
-
- return
+ return (
+
+
+
+ )
}
diff --git a/packages/solid/src/components/floating-panel/floating-panel-positioner.tsx b/packages/solid/src/components/floating-panel/floating-panel-positioner.tsx
index 8aa98ef66f..9be83c73a7 100644
--- a/packages/solid/src/components/floating-panel/floating-panel-positioner.tsx
+++ b/packages/solid/src/components/floating-panel/floating-panel-positioner.tsx
@@ -1,4 +1,5 @@
import { mergeProps } from '@zag-js/solid'
+import { Show } from 'solid-js'
import { type HTMLProps, type PolymorphicProps, ark } from '../factory.tsx'
import { usePresenceContext } from '../presence/index.tsx'
import { useFloatingPanelContext } from './use-floating-panel-context.ts'
@@ -11,9 +12,9 @@ export const FloatingPanelPositioner = (props: FloatingPanelPositionerProps) =>
const mergedProps = mergeProps(() => floatingPanel().getPositionerProps(), props)
const presence = usePresenceContext()
- if (presence().unmounted) {
- return null
- }
-
- return
+ return (
+
+
+
+ )
}
diff --git a/packages/solid/src/components/floating-panel/tests/basic.tsx b/packages/solid/src/components/floating-panel/tests/basic.tsx
new file mode 100644
index 0000000000..f40f7098a3
--- /dev/null
+++ b/packages/solid/src/components/floating-panel/tests/basic.tsx
@@ -0,0 +1,19 @@
+import { FloatingPanel } from '@ark-ui/solid/floating-panel'
+import { Portal } from 'solid-js/web'
+
+export const ComponentUnderTest = (props: FloatingPanel.RootProps) => (
+
+ Open Panel
+
+
+
+
+ Floating Panel
+ Close
+
+ Panel Body
+
+
+
+
+)
diff --git a/packages/solid/src/components/floating-panel/tests/floating-panel.test.tsx b/packages/solid/src/components/floating-panel/tests/floating-panel.test.tsx
new file mode 100644
index 0000000000..4851744bc4
--- /dev/null
+++ b/packages/solid/src/components/floating-panel/tests/floating-panel.test.tsx
@@ -0,0 +1,52 @@
+import { render, screen, waitFor } from '@solidjs/testing-library'
+import user from '@testing-library/user-event'
+import { ComponentUnderTest } from './basic.tsx'
+
+describe('FloatingPanel', () => {
+ it('should show panel content when opened', async () => {
+ render(() => )
+
+ await user.click(screen.getByText('Open Panel'))
+ expect(await screen.findByText('Panel Body')).toBeVisible()
+
+ await user.click(screen.getByText('Close'))
+ await waitFor(() => expect(screen.queryByText('Panel Body')).not.toBeVisible())
+ })
+
+ it('should invoke onOpenChange if panel is closed', async () => {
+ const onOpenChange = vi.fn()
+ render(() => )
+ await user.click(screen.getByText('Close'))
+
+ expect(onOpenChange).toHaveBeenCalledTimes(1)
+ })
+
+ it('should be able to lazy mount', async () => {
+ render(() => )
+
+ expect(screen.queryByTestId('positioner')).not.toBeInTheDocument()
+
+ await user.click(screen.getByRole('button', { name: 'Open Panel' }))
+ expect(screen.getByTestId('positioner')).toBeInTheDocument()
+
+ await user.click(screen.getByRole('button', { name: 'Close Window' }))
+ expect(screen.getByTestId('positioner')).toBeInTheDocument()
+ })
+
+ it('should not have aria-controls if lazy mounted', async () => {
+ render(() => )
+ expect(screen.getByRole('button', { name: 'Open Panel' })).not.toHaveAttribute('aria-controls')
+ })
+
+ it('should lazy mount and unmount on exit', async () => {
+ render(() => )
+
+ expect(screen.queryByTestId('positioner')).not.toBeInTheDocument()
+
+ await user.click(screen.getByRole('button', { name: 'Open Panel' }))
+ expect(screen.getByTestId('positioner')).toBeInTheDocument()
+
+ await user.click(screen.getByRole('button', { name: 'Close Window' }))
+ await waitFor(() => expect(screen.queryByTestId('positioner')).not.toBeInTheDocument())
+ })
+})