diff --git a/.changeset/early-ghosts-fix.md b/.changeset/early-ghosts-fix.md new file mode 100644 index 0000000000..b0b21239d7 --- /dev/null +++ b/.changeset/early-ghosts-fix.md @@ -0,0 +1,6 @@ +--- +"@nextui-org/modal": patch +"@nextui-org/use-disclosure": patch +--- + +Added useEffect in useModal to fire onOpenChange and in useDisclosure, onOpenChange now accepts onOpen as parameter(#3887) diff --git a/packages/components/modal/__tests__/modal.test.tsx b/packages/components/modal/__tests__/modal.test.tsx index b207a2269c..619570ea62 100644 --- a/packages/components/modal/__tests__/modal.test.tsx +++ b/packages/components/modal/__tests__/modal.test.tsx @@ -1,8 +1,9 @@ import * as React from "react"; import {render, fireEvent} from "@testing-library/react"; import userEvent from "@testing-library/user-event"; +import {Button} from "@nextui-org/button"; -import {Modal, ModalContent, ModalBody, ModalHeader, ModalFooter} from "../src"; +import {Modal, ModalContent, ModalBody, ModalHeader, ModalFooter, useDisclosure} from "../src"; // e.g. console.error Warning: Function components cannot be given refs. // Attempts to access this ref will fail. Did you mean to use React.forwardRef()? @@ -91,6 +92,38 @@ describe("Modal", () => { expect(onClose).toHaveBeenCalled(); }); + const ModalWrapper = ({onOpenChange}) => { + const {isOpen, onOpen} = useDisclosure(); + + return ( + <> + + + + Modal header + Modal body + Modal footer + + + + ); + }; + + test("should fire 'onOpenChange' callback when open button is clicked and modal opens", async () => { + const onOpenChange = jest.fn(); + + const {getByLabelText} = render(); + + const openButton = getByLabelText("Open"); + const user = userEvent.setup(); + + await user.click(openButton); + + expect(onOpenChange).toHaveBeenCalled(); + }); + it("should hide the modal when pressing the escape key", () => { const onClose = jest.fn(); diff --git a/packages/components/modal/src/use-modal.ts b/packages/components/modal/src/use-modal.ts index 66b6f7be63..931ce36076 100644 --- a/packages/components/modal/src/use-modal.ts +++ b/packages/components/modal/src/use-modal.ts @@ -3,7 +3,7 @@ import type {HTMLMotionProps} from "framer-motion"; import {AriaModalOverlayProps} from "@react-aria/overlays"; import {useAriaModalOverlay} from "@nextui-org/use-aria-modal-overlay"; -import {useCallback, useId, useRef, useState, useMemo, ReactNode} from "react"; +import {useCallback, useId, useRef, useState, useMemo, ReactNode, useEffect} from "react"; import {modal} from "@nextui-org/theme"; import { HTMLNextUIProps, @@ -129,6 +129,12 @@ export function useModal(originalProps: UseModalProps) { }, }); + useEffect(() => { + if (isOpen) { + onOpenChange?.(isOpen); + } + }, [isOpen]); + const {modalProps, underlayProps} = useAriaModalOverlay( { isDismissable, diff --git a/packages/hooks/use-disclosure/src/index.ts b/packages/hooks/use-disclosure/src/index.ts index c5bccc11df..91675e52e2 100644 --- a/packages/hooks/use-disclosure/src/index.ts +++ b/packages/hooks/use-disclosure/src/index.ts @@ -44,11 +44,14 @@ export function useDisclosure(props: UseDisclosureProps = {}) { onOpenPropCallbackRef?.(); }, [isControlled, onOpenPropCallbackRef]); - const onOpenChange = useCallback(() => { - const action = isOpen ? onClose : onOpen; + const onOpenChange = useCallback( + (isOpen: boolean) => { + const action = isOpen ? onOpen : onClose; - action(); - }, [isOpen, onOpen, onClose]); + action(); + }, + [isOpen, onOpen, onClose], + ); return { isOpen: !!isOpen,