diff --git a/src/components/popover-container/popover-container.component.tsx b/src/components/popover-container/popover-container.component.tsx index cb52b7399b..8ae562d6d1 100644 --- a/src/components/popover-container/popover-container.component.tsx +++ b/src/components/popover-container/popover-container.component.tsx @@ -1,6 +1,7 @@ import React, { useEffect, useRef, useState } from "react"; import { PaddingProps } from "styled-system"; import { Transition, TransitionStatus } from "react-transition-group"; +import { OffsetsFunction } from "@popperjs/core/lib/modifiers/offset"; import { PopoverContainerWrapperStyle, @@ -11,6 +12,7 @@ import { PopoverContainerOpenIcon, } from "./popover-container.style"; import Icon from "../icon"; +import Popover from "../../__internal__/popover"; import createGuid from "../../__internal__/utils/helpers/guid"; import { filterStyledSystemPaddingProps } from "../../style/utils"; import ClickAwayWrapper from "../../__internal__/click-away-wrapper"; @@ -109,6 +111,19 @@ export interface PopoverContainerProps extends PaddingProps { containerAriaLabel?: string; } +const offset: OffsetsFunction = ({ reference }) => { + return [0, -reference.height]; +}; + +const popperModifiers = [ + { + name: "offset", + options: { + offset, + }, + }, +]; + export const PopoverContainer = ({ children, title, @@ -140,7 +155,8 @@ export const PopoverContainer = ({ const isOpen = isControlled ? open : isOpenInternal; useEffect(() => { - if (isOpen && closeButtonRef.current) closeButtonRef.current.focus(); + if (isOpen && closeButtonRef.current) + setTimeout(() => closeButtonRef.current?.focus(), 0); }, [isOpen]); const handleOpenButtonClick = (e: React.MouseEvent) => { @@ -204,32 +220,38 @@ export const PopoverContainer = ({ unmountOnExit nodeRef={popoverContentNodeRef} > - {(state: TransitionStatus) => ( - - - + isOpen && ( + + - {title} - - {renderCloseComponent(renderCloseComponentProps)} - - {children} - - )} + + + {title} + + {renderCloseComponent(renderCloseComponentProps)} + + {children} + + + ) + } diff --git a/src/components/popover-container/popover-container.spec.tsx b/src/components/popover-container/popover-container.spec.tsx index 41283e236c..32384b9922 100644 --- a/src/components/popover-container/popover-container.spec.tsx +++ b/src/components/popover-container/popover-container.spec.tsx @@ -3,6 +3,9 @@ import React, { forwardRef } from "react"; import { mount, ReactWrapper } from "enzyme"; import { act } from "react-dom/test-utils"; import { Transition } from "react-transition-group"; +import { OffsetsFunction } from "@popperjs/core/lib/modifiers/offset"; + +import Popover from "../../__internal__/popover"; import { PopoverContainerContentStyle, PopoverContainerCloseIcon, @@ -161,28 +164,58 @@ describe("PopoverContainer", () => { expect(wrapper.find("button").props().tabIndex).toBe(0); }); - it("`position` should be right by default", () => { + it("`shouldCoverButton` should be false by default", () => { act(() => { wrapper.find(PopoverContainerOpenIcon).props().onAction(); }); wrapper.update(); - expect(wrapper.find(PopoverContainerContentStyle).props().position).toBe( - "right" + expect(wrapper.find(PopoverContainerContentStyle).props().modifiers).toBe( + undefined ); }); - it("`shouldCoverButton` should be false by default", () => { - act(() => { - wrapper.find(PopoverContainerOpenIcon).props().onAction(); + describe("popover", () => { + it("renders a DayPicker inside of a Popover", () => { + wrapper = render({ open: true }); + + expect( + wrapper.find(Popover).find(PopoverContainerContentStyle).exists() + ).toBe(true); }); - wrapper.update(); + it("should have the correct offset when shouldCoverButton is set to true", () => { + wrapper = render({ shouldCoverButton: true, open: true }); - expect( - wrapper.find(PopoverContainerContentStyle).props().shouldCoverButton - ).toBe(false); + expect( + wrapper.find(Popover).props().modifiers?.[0]?.options?.offset + ).not.toBe(undefined); + + const reference = { height: 40, width: 0, y: 0, x: 0 }; + const placement = "bottom"; + + expect( + (wrapper.find(Popover).props().modifiers?.[0]?.options + ?.offset as OffsetsFunction)({ + placement, + reference, + popper: reference, + }) + ).toEqual([0, -40]); + }); + + it.each([ + ["bottom-start", "right"] as const, + ["bottom-end", "left"] as const, + ])( + "should have placement equal to %s when position prop is equal %s", + (placement, position) => { + wrapper = render({ position, open: true }); + + expect(wrapper.find(Popover).props().placement).toEqual(placement); + } + ); }); describe("if is controlled", () => { @@ -435,6 +468,7 @@ describe("PopoverContainer", () => { }); describe("and custom component is provided as a closing button", () => { + jest.useFakeTimers(); interface MyCloseButtonProps extends RenderCloseProps { children: React.ReactNode; } @@ -485,6 +519,7 @@ describe("PopoverContainer", () => { }); it("should be focused if `ref` is provided", () => { + jest.runAllTimers(); expect(wrapper.find(MyCloseButton)).toBeFocused(); }); @@ -566,12 +601,6 @@ describe("PopoverContainerContentStyle", () => { expect(wrapper.find(PopoverContainerContentStyle).props().role).toBe( "dialog" ); - expect(wrapper.find(PopoverContainerContentStyle).props().position).toBe( - "right" - ); - expect( - wrapper.find(PopoverContainerContentStyle).props().shouldCoverButton - ).toBe(false); expect( wrapper.find(PopoverContainerContentStyle).prop("aria-labelledby") ).toContain("PopoverContainer_guid-123"); @@ -599,39 +628,6 @@ describe("PopoverContainerContentStyle", () => { ); }); - it("should render to the right by default", () => { - const wrapper = mount(); - - assertStyleMatch( - { - left: "0", - }, - wrapper - ); - }); - - it("should render to the left if position is set to `left`", () => { - const wrapper = mount(); - - assertStyleMatch( - { - right: "0", - }, - wrapper - ); - }); - - it("should render correct style if `shouldCoverButton` prop is provided", () => { - const wrapper = mount(); - - assertStyleMatch( - { - top: "0", - }, - wrapper - ); - }); - describe("should render correct style of animation", () => { it("if the animation has state `entered`", () => { const wrapper = mount( diff --git a/src/components/popover-container/popover-container.style.ts b/src/components/popover-container/popover-container.style.ts index 19fd5fa3b2..112dbe2e0e 100644 --- a/src/components/popover-container/popover-container.style.ts +++ b/src/components/popover-container/popover-container.style.ts @@ -19,8 +19,6 @@ const PopoverContainerHeaderStyle = styled.div` `; type PopoverContainerContentStyleProps = { - shouldCoverButton?: boolean; - position?: "left" | "right"; animationState?: TransitionStatus; }; @@ -33,10 +31,6 @@ const PopoverContainerContentStyle = styled.div theme.zIndex.popover}; - ${({ shouldCoverButton }) => shouldCoverButton && "top: 0"} - - ${({ position }) => (position === "left" ? "right: 0" : "left: 0")}; - ${({ animationState }) => { switch (animationState) { case "entering":