✨(react) add a popover component
A design system needs a popover mecanism. In the select component, this logic is handled by downshift. We introduce a new component, responsible to open an element in a popin and close it when the user click outside of it.
This commit is contained in:
committed by
aleb_the_flash
parent
f967775fb6
commit
1d1cf81cf6
5
.changeset/spotty-moons-battle.md
Normal file
5
.changeset/spotty-moons-battle.md
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
---
|
||||||
|
"@openfun/cunningham-react": minor
|
||||||
|
---
|
||||||
|
|
||||||
|
add a popover component
|
||||||
11
packages/react/src/components/Popover/index.scss
Normal file
11
packages/react/src/components/Popover/index.scss
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
.c__popover {
|
||||||
|
position: absolute;
|
||||||
|
display: flex;
|
||||||
|
width: fit-content;
|
||||||
|
z-index: 1;
|
||||||
|
|
||||||
|
&:not(&--borderless) {
|
||||||
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.3);
|
||||||
|
background-color: var(--c--components--forms-datepicker--menu-background-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
36
packages/react/src/components/Popover/index.stories.tsx
Normal file
36
packages/react/src/components/Popover/index.stories.tsx
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
import React, { useRef, useState } from "react";
|
||||||
|
|
||||||
|
import { Meta } from "@storybook/react";
|
||||||
|
import { Button } from ":/components/Button";
|
||||||
|
import { Popover } from ":/components/Popover/index";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: "Components/Popover",
|
||||||
|
component: Popover,
|
||||||
|
} as Meta<typeof Popover>;
|
||||||
|
|
||||||
|
export const Default = () => {
|
||||||
|
const [isOpen, setIsOpen] = useState(false);
|
||||||
|
const parentRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div ref={parentRef} style={{ width: "fit-content" }}>
|
||||||
|
<Button onClick={() => setIsOpen(!isOpen)}>Toggle</Button>
|
||||||
|
{isOpen && (
|
||||||
|
<Popover parentRef={parentRef} onClickOutside={() => setIsOpen(false)}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
height: "200px",
|
||||||
|
width: "200px",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
I am open
|
||||||
|
</div>
|
||||||
|
</Popover>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
28
packages/react/src/components/Popover/index.tsx
Normal file
28
packages/react/src/components/Popover/index.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import React, { PropsWithChildren, RefObject } from "react";
|
||||||
|
import classNames from "classnames";
|
||||||
|
import { useHandleClickOutside } from ":/hooks/useHandleClickOutside";
|
||||||
|
|
||||||
|
type PopoverProps = PropsWithChildren & {
|
||||||
|
parentRef: RefObject<HTMLDivElement>;
|
||||||
|
onClickOutside: () => void;
|
||||||
|
borderless?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Popover = ({
|
||||||
|
parentRef,
|
||||||
|
children,
|
||||||
|
onClickOutside,
|
||||||
|
borderless = false,
|
||||||
|
}: PopoverProps) => {
|
||||||
|
useHandleClickOutside(parentRef, onClickOutside);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={classNames("c__popover", {
|
||||||
|
"c__popover--borderless": borderless,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
18
packages/react/src/hooks/useHandleClickOutside.ts
Normal file
18
packages/react/src/hooks/useHandleClickOutside.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { RefObject, useEffect } from "react";
|
||||||
|
|
||||||
|
export const useHandleClickOutside = (
|
||||||
|
ref: RefObject<HTMLDivElement>,
|
||||||
|
onClickOutside: any
|
||||||
|
) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const outsideListenerEvent = (event: MouseEvent) => {
|
||||||
|
if (!ref.current || ref.current.contains(event.target as Node)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onClickOutside();
|
||||||
|
};
|
||||||
|
document.addEventListener("click", outsideListenerEvent, true);
|
||||||
|
return () =>
|
||||||
|
document.removeEventListener("click", outsideListenerEvent, true);
|
||||||
|
}, []);
|
||||||
|
};
|
||||||
@@ -13,6 +13,7 @@
|
|||||||
@import './components/Forms/Switch';
|
@import './components/Forms/Switch';
|
||||||
@import './components/Loader';
|
@import './components/Loader';
|
||||||
@import './components/Pagination';
|
@import './components/Pagination';
|
||||||
|
@import './components/Popover';
|
||||||
|
|
||||||
* {
|
* {
|
||||||
font-family: var(--c--theme--font--families--base);
|
font-family: var(--c--theme--font--families--base);
|
||||||
|
|||||||
Reference in New Issue
Block a user