Skip to content

Commit

Permalink
fix(popover-container): position automatically depending on available…
Browse files Browse the repository at this point in the history
… space
  • Loading branch information
mkrds committed Jun 27, 2022
1 parent 9ea37a1 commit 4b63a17
Show file tree
Hide file tree
Showing 3 changed files with 93 additions and 81 deletions.
74 changes: 48 additions & 26 deletions src/components/popover-container/popover-container.component.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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";
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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<HTMLElement>) => {
Expand Down Expand Up @@ -204,32 +220,38 @@ export const PopoverContainer = ({
unmountOnExit
nodeRef={popoverContentNodeRef}
>
{(state: TransitionStatus) => (
<PopoverContainerContentStyle
data-element="popover-container-content"
role="dialog"
animationState={state}
position={position}
shouldCoverButton={shouldCoverButton}
aria-labelledby={popoverContainerId}
aria-label={containerAriaLabel}
aria-describedby={ariaDescribedBy}
p="16px 24px"
ref={popoverContentNodeRef}
{...filterStyledSystemPaddingProps(rest)}
>
<PopoverContainerHeaderStyle>
<PopoverContainerTitleStyle
id={popoverContainerId}
data-element="popover-container-title"
{(state: TransitionStatus) =>
isOpen && (
<Popover
reference={openButtonRef}
placement={position === "right" ? "bottom-start" : "bottom-end"}
{...(shouldCoverButton && { modifiers: popperModifiers })}
>
<PopoverContainerContentStyle
data-element="popover-container-content"
role="dialog"
animationState={state}
aria-labelledby={popoverContainerId}
aria-label={containerAriaLabel}
aria-describedby={ariaDescribedBy}
p="16px 24px"
ref={popoverContentNodeRef}
{...filterStyledSystemPaddingProps(rest)}
>
{title}
</PopoverContainerTitleStyle>
{renderCloseComponent(renderCloseComponentProps)}
</PopoverContainerHeaderStyle>
{children}
</PopoverContainerContentStyle>
)}
<PopoverContainerHeaderStyle>
<PopoverContainerTitleStyle
id={popoverContainerId}
data-element="popover-container-title"
>
{title}
</PopoverContainerTitleStyle>
{renderCloseComponent(renderCloseComponentProps)}
</PopoverContainerHeaderStyle>
{children}
</PopoverContainerContentStyle>
</Popover>
)
}
</Transition>
</PopoverContainerWrapperStyle>
</ClickAwayWrapper>
Expand Down
94 changes: 45 additions & 49 deletions src/components/popover-container/popover-container.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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", () => {
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -485,6 +519,7 @@ describe("PopoverContainer", () => {
});

it("should be focused if `ref` is provided", () => {
jest.runAllTimers();
expect(wrapper.find(MyCloseButton)).toBeFocused();
});

Expand Down Expand Up @@ -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");
Expand Down Expand Up @@ -599,39 +628,6 @@ describe("PopoverContainerContentStyle", () => {
);
});

it("should render to the right by default", () => {
const wrapper = mount(<PopoverContainerContentStyle />);

assertStyleMatch(
{
left: "0",
},
wrapper
);
});

it("should render to the left if position is set to `left`", () => {
const wrapper = mount(<PopoverContainerContentStyle position="left" />);

assertStyleMatch(
{
right: "0",
},
wrapper
);
});

it("should render correct style if `shouldCoverButton` prop is provided", () => {
const wrapper = mount(<PopoverContainerContentStyle shouldCoverButton />);

assertStyleMatch(
{
top: "0",
},
wrapper
);
});

describe("should render correct style of animation", () => {
it("if the animation has state `entered`", () => {
const wrapper = mount(
Expand Down
6 changes: 0 additions & 6 deletions src/components/popover-container/popover-container.style.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ const PopoverContainerHeaderStyle = styled.div`
`;

type PopoverContainerContentStyleProps = {
shouldCoverButton?: boolean;
position?: "left" | "right";
animationState?: TransitionStatus;
};

Expand All @@ -33,10 +31,6 @@ const PopoverContainerContentStyle = styled.div<PopoverContainerContentStyleProp
position: absolute;
z-index: ${({ theme }) => theme.zIndex.popover};
${({ shouldCoverButton }) => shouldCoverButton && "top: 0"}
${({ position }) => (position === "left" ? "right: 0" : "left: 0")};
${({ animationState }) => {
switch (animationState) {
case "entering":
Expand Down

0 comments on commit 4b63a17

Please sign in to comment.