diff --git a/.changeset/little-llamas-swim.md b/.changeset/little-llamas-swim.md new file mode 100644 index 0000000000..c2a34bdd1f --- /dev/null +++ b/.changeset/little-llamas-swim.md @@ -0,0 +1,6 @@ +--- +"@twilio-paste/core": patch +"@twilio-paste/non-modal-dialog-primitive": patch +--- + +[Non-modal Dialog Primitive] Fix to always use a portal when rendering, even when using the state hook. diff --git a/packages/paste-core/primitives/non-modal-dialog/__tests__/NonModalDialogPrimitive.tsx b/packages/paste-core/primitives/non-modal-dialog/__tests__/NonModalDialogPrimitive.tsx new file mode 100644 index 0000000000..7ff1625a93 --- /dev/null +++ b/packages/paste-core/primitives/non-modal-dialog/__tests__/NonModalDialogPrimitive.tsx @@ -0,0 +1,49 @@ +import { act, fireEvent, render, screen } from "@testing-library/react"; +import * as React from "react"; + +import { + NonModalDialogArrowPrimitive, + NonModalDialogDisclosurePrimitive, + NonModalDialogPrimitive, + type NonModalDialogPrimitivePopoverInitialState, + useNonModalDialogPrimitiveState, +} from "../src"; + +const Example: React.FC<{ options?: NonModalDialogPrimitivePopoverInitialState }> = ({ + options = { placement: "bottom-end" }, +}) => { + const popover = useNonModalDialogPrimitiveState(options); + return ( +
+ Open Popover + + + Welcome to Reakit! + +
+ ); +}; +Example.displayName = "Example"; + +describe("NonModalDialogPrimitive", () => { + describe("portal behavior", () => { + test("renders in a portal under when nothing is provided into hook", async () => { + render(); + + const wrapper = screen.getByTestId("wrapper"); + const button = screen.getByText("Open Popover"); + const popover = screen.getByText("Welcome to Reakit!"); + expect(wrapper).toBeInTheDocument(); + expect(popover).not.toBeVisible(); + + await act(async () => { + fireEvent.click(button); + }); + + expect(popover).toBeVisible(); + + // Check if popover is in portal + expect(wrapper).not.toContainElement(popover); + }); + }); +}); diff --git a/packages/paste-core/primitives/non-modal-dialog/src/index.tsx b/packages/paste-core/primitives/non-modal-dialog/src/index.tsx index 694cda4497..0c51b99d46 100644 --- a/packages/paste-core/primitives/non-modal-dialog/src/index.tsx +++ b/packages/paste-core/primitives/non-modal-dialog/src/index.tsx @@ -1,14 +1,28 @@ +import { + type PopoverInitialState as NonModalDialogPrimitivePopoverInitialState, + type PopoverStateReturn as NonModalDialogPrimitiveStateReturn, + usePopoverState as _usePopoverState, +} from "@twilio-paste/reakit-library"; + +/* + * Fixes issue where the popover would not open in a portal + * when using the state hook directly + */ +export const useNonModalDialogPrimitiveState = ( + options: NonModalDialogPrimitivePopoverInitialState, +): NonModalDialogPrimitiveStateReturn => { + return _usePopoverState({ modal: true, ...options }); +}; + export { - usePopoverState as useNonModalDialogPrimitiveState, Popover as NonModalDialogPrimitive, PopoverDisclosure as NonModalDialogDisclosurePrimitive, PopoverArrow as NonModalDialogArrowPrimitive, } from "@twilio-paste/reakit-library"; +export type { NonModalDialogPrimitivePopoverInitialState, NonModalDialogPrimitiveStateReturn }; export type { PopoverState as NonModalDialogPrimitiveState, - PopoverStateReturn as NonModalDialogPrimitiveStateReturn, - PopoverInitialState as NonModalDialogPrimitivePopoverInitialState, PopoverProps as NonModalDialogPrimitiveProps, PopoverDisclosureProps as NonModalDialogDisclosurePrimitiveProps, PopoverArrowProps as NonModalDialogArrowPrimitiveProps,