diff --git a/src/components/feedback/Popover.tsx b/src/components/feedback/Popover.tsx index f5eb66b2..5d09f100 100644 --- a/src/components/feedback/Popover.tsx +++ b/src/components/feedback/Popover.tsx @@ -164,7 +164,7 @@ export type PopoverProps = { classes?: string | string[]; variant?: 'panel' | 'custom'; // TODO 'tooltip' - /** Whether the popover is currently open or not. Defaults to false */ + /** Whether the popover is currently open or not */ open: boolean; /** The element relative to which the popover should be positioned */ anchorElementRef: RefObject; diff --git a/src/pattern-library/components/patterns/feedback/PopoverPage.tsx b/src/pattern-library/components/patterns/feedback/PopoverPage.tsx new file mode 100644 index 00000000..50175a68 --- /dev/null +++ b/src/pattern-library/components/patterns/feedback/PopoverPage.tsx @@ -0,0 +1,176 @@ +import { Link } from '../../../../components/navigation'; +import Library from '../../Library'; + +export default function PopoverPage() { + return ( + +

+ Popover is a floating component that can be shown + next/relative to another anchor element. +

+ + } + > + +

+ By default, Popover will be displayed under the + anchor element, unless there is not enough space below. +

+

+ It will always be at least as big as the anchor element, but it + can grow to fit its content if needed. However, it will never grow + or be displayed outside of the viewport. +

+

+ In browsers that support it, Popover uses the{' '} + + popover + {' '} + attribute and gets toggled via{' '} + + popover API + + . Otherwise, it is rendered as an absolute-positioned element, so + it is recommended to wrap it and its anchor element in a + relative-positioned container. +

+ + } + > + + + + + + + + + + + + Determines to what side of the anchor element should the popover + be aligned. + + + {"'left' | 'right'"} + + + {"'left'"} + + + + + + + + A reference to the element to which the popover should anchor, + which will be used to calculate the popover size and + positioning. + + + {'RefObject'} + + + + + + + Determines if the{' '} + + popover API + {' '} + should be used. It{"'"}s mainly used as a test seam, but can + help explicitly disabling this behavior if needed. + + + boolean + + + true if the browser supports [popover] + . Otherwise it is false + + + + + + + Additional CSS classes to pass to the popover. + + + string | string[] + + + undefined + + + + + + + Whether the Popover is currently open or not. + + + boolean + + + + + + + Determines if focus should be restored when the{' '} + Popover is closed. + + + boolean + + + false + + + + + + + Set the Popover theming. + + + {"'panel' | 'custom'"} + + + {"'panel'"} + + + + + +
+
+ ); +} diff --git a/src/pattern-library/components/patterns/input/SelectPage.tsx b/src/pattern-library/components/patterns/input/SelectPage.tsx index 821e964c..31dc7b60 100644 --- a/src/pattern-library/components/patterns/input/SelectPage.tsx +++ b/src/pattern-library/components/patterns/input/SelectPage.tsx @@ -126,21 +126,9 @@ export default function SelectPage() { {"'"} UI can be customized and values can be objects.

- In browsers that support it, the listbox uses the{' '} - - popover - {' '} - attribute and gets toggled via{' '} - - popover API - - . Otherwise, it is rendered as an absolute-positioned element. + A Popover{' '} + component is used to wrap the listbox and ensure it is always + correctly positioned.

diff --git a/src/pattern-library/examples/popover-basic.tsx b/src/pattern-library/examples/popover-basic.tsx new file mode 100644 index 00000000..e14a2a4f --- /dev/null +++ b/src/pattern-library/examples/popover-basic.tsx @@ -0,0 +1,27 @@ +import { useRef, useState } from 'preact/hooks'; + +import { Popover } from '../../components/feedback'; +import { Button } from '../../components/input'; +import { useClickAway } from '../../hooks/use-click-away'; + +export default function App() { + const [open, setOpen] = useState(false); + const buttonRef = useRef(null); + + useClickAway(buttonRef, () => setOpen(false)); + + return ( +
+ + + The content of the popover goes here + +
+ ); +} diff --git a/src/pattern-library/examples/popover-custom.tsx b/src/pattern-library/examples/popover-custom.tsx new file mode 100644 index 00000000..7440d1f3 --- /dev/null +++ b/src/pattern-library/examples/popover-custom.tsx @@ -0,0 +1,32 @@ +import { useRef, useState } from 'preact/hooks'; + +import { Popover } from '../../components/feedback'; +import { Button } from '../../components/input'; +import { useClickAway } from '../../hooks/use-click-away'; + +export default function App() { + const [open, setOpen] = useState(false); + const buttonRef = useRef(null); + + useClickAway(buttonRef, () => setOpen(false)); + + return ( +
+ + + This popover has been customized + +
+ ); +} diff --git a/src/pattern-library/examples/popover-right.tsx b/src/pattern-library/examples/popover-right.tsx new file mode 100644 index 00000000..412dbd86 --- /dev/null +++ b/src/pattern-library/examples/popover-right.tsx @@ -0,0 +1,32 @@ +import { useRef, useState } from 'preact/hooks'; + +import { Popover } from '../../components/feedback'; +import { Button } from '../../components/input'; +import { useClickAway } from '../../hooks/use-click-away'; + +export default function App() { + const [open, setOpen] = useState(false); + const buttonRef = useRef(null); + + useClickAway(buttonRef, () => setOpen(false)); + + return ( +
+ + + The content of the popover goes here + +
+ ); +} diff --git a/src/pattern-library/routes.ts b/src/pattern-library/routes.ts index 385d180f..798faf76 100644 --- a/src/pattern-library/routes.ts +++ b/src/pattern-library/routes.ts @@ -13,6 +13,7 @@ import TablePage from './components/patterns/data/TablePage'; import ThumbnailPage from './components/patterns/data/ThumbnailPage'; import CalloutPage from './components/patterns/feedback/CalloutPage'; import DialogPage from './components/patterns/feedback/DialogPage'; +import PopoverPage from './components/patterns/feedback/PopoverPage'; import SpinnerPage from './components/patterns/feedback/SpinnerPage'; import ToastMessagesPage from './components/patterns/feedback/ToastMessagesPage'; import UseClickAwayPage from './components/patterns/hooks/UseClickAwayPage'; @@ -146,6 +147,12 @@ const routes: PlaygroundRoute[] = [ component: DialogPage, route: '/feedback-dialog', }, + { + title: 'Popover', + group: 'feedback', + component: PopoverPage, + route: '/feedback-popover', + }, { title: 'Spinner', group: 'feedback',