/* * Copyright 2024 Palantir Technologies, Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { render } from "@testing-library/react"; import { createRef, useEffect, useId, useMemo } from "react"; import { afterEach, beforeAll, describe, expect, it, vi } from "@blueprintjs/test-commons/vitest"; import type { OverlayProps } from "../components/overlay/overlayProps"; import type { OverlayInstance } from "../components/overlay2/overlayInstance"; import { OverlaysProvider } from "../context"; import { modifyGlobalStack } from "./overlays/useLegacyOverlayStack"; import { useOverlayStack, usePrevious } from "./"; interface TestComponentProps extends OverlayProps { handleLastOpenedChange?: (lastOpened: OverlayInstance | undefined) => void; containerRef: React.RefObject; } const TestComponentWithoutProvider: React.FC = ({ autoFocus, children, enforceFocus, containerRef: containerElement, handleLastOpenedChange, hasBackdrop, isOpen, usePortal, }) => { const { openOverlay, getLastOpened, closeOverlay } = useOverlayStack(); const id = useId(); const instance = useMemo( () => ({ containerElement, id, props: { autoFocus, enforceFocus, hasBackdrop, usePortal, }, }), [autoFocus, containerElement, enforceFocus, hasBackdrop, id, usePortal], ); const prevIsOpen = usePrevious(isOpen) ?? false; useEffect(() => { if (!prevIsOpen && isOpen) { // just opened openOverlay(instance); } if (prevIsOpen && !isOpen) { // just closed closeOverlay(id); } }, [isOpen, openOverlay, closeOverlay, prevIsOpen, instance, id]); // run once on unmount useEffect(() => { return () => { if (isOpen) { closeOverlay(id); } }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const lastOpened = getLastOpened(); const prevLastOpened = usePrevious(lastOpened); useEffect(() => { if (prevLastOpened !== lastOpened) { handleLastOpenedChange?.(lastOpened); } }, [handleLastOpenedChange, lastOpened, prevLastOpened]); return
{children}
; }; const TestComponentWithProvider: React.FC = props => { return ( ); }; describe("useOverlayStack()", () => { const handleLastOpenedChange = vi.fn(); const containerRef = createRef(); const TEST_PROPS_CLOSED: TestComponentProps = { autoFocus: true, containerRef, enforceFocus: true, handleLastOpenedChange, hasBackdrop: true, isOpen: false, usePortal: true, }; const TEST_PROPS_OPEN: TestComponentProps = { ...TEST_PROPS_CLOSED, isOpen: true, }; afterEach(() => { handleLastOpenedChange.mockClear(); }); describe("with ", () => { it("should render without crashing", () => { render(); }); it("opening an overlay should change the result of getLastOpened()", () => { const { rerender } = render(); // we need to re-render twice: the overlay is added to the stack _after_ the first re-render completes, // so it wont' trigger a change in getLastOpened() until the second re-render. rerender(); rerender(); expect(handleLastOpenedChange).toHaveBeenCalledOnce(); const lastOpenedInstance = handleLastOpenedChange.mock.calls[0][0] as OverlayInstance; expect(lastOpenedInstance).to.exist; expect(containerRef.current).to.exist; expect(lastOpenedInstance.containerElement.current).to.equal(containerRef.current); }); }); describe("without ", () => { beforeAll(() => { // ensure there is a clean global state that might be polluted by other test suites modifyGlobalStack(s => s.splice(0, s.length)); }); it("should render without crashing", () => { render(); }); it("opening an overlay should change the result of getLastOpened()", () => { const { rerender } = render(); // we need to re-render twice: the overlay is added to the stack _after_ the first re-render completes, // so it wont' trigger a change in getLastOpened() until the second re-render. rerender(); rerender(); expect(handleLastOpenedChange).toHaveBeenCalled(); const lastOpenedInstance = handleLastOpenedChange.mock.calls[0][0] as OverlayInstance; expect(lastOpenedInstance).to.exist; expect(containerRef.current).to.exist; expect( lastOpenedInstance.containerElement.current === containerRef.current, "expected last opened overlay container element ref to be the same as the ref passed through props", ).to.be.true; }); }); });