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()) + }) +})