Skip to content

Commit 25265c1

Browse files
author
Alejandro Celaya
committed
Add documentation page for Popover component
1 parent 54d82ea commit 25265c1

File tree

7 files changed

+278
-16
lines changed

7 files changed

+278
-16
lines changed

src/components/feedback/Popover.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,7 @@ export type PopoverProps = {
164164
classes?: string | string[];
165165
variant?: 'panel' | 'custom';
166166

167-
/** Whether the popover is currently open or not. Defaults to false */
167+
/** Whether the popover is currently open or not */
168168
open: boolean;
169169
/** The element relative to which the popover should be positioned */
170170
anchorElementRef: RefObject<HTMLElement | undefined>;
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
import { Link } from '../../../../components/navigation';
2+
import Library from '../../Library';
3+
4+
export default function PopoverPage() {
5+
return (
6+
<Library.Page
7+
title="Popover"
8+
intro={
9+
<>
10+
<p>
11+
<code>Popover</code> is a floating element rendered above other
12+
content and positioned next to an anchor element.
13+
</p>
14+
</>
15+
}
16+
>
17+
<Library.Section
18+
title="Popover"
19+
intro={
20+
<>
21+
<p>
22+
By default, <code>Popover</code> will be displayed below the
23+
anchor element, unless there is not enough space below.
24+
</p>
25+
<p>
26+
It will always be at least as wide as the anchor element, but it
27+
can grow to fit its content if needed. However, it will never grow
28+
beyond the viewport.
29+
</p>
30+
<p>
31+
In browsers that support it, <code>Popover</code> uses the{' '}
32+
<Link
33+
target="_blank"
34+
href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/popover"
35+
>
36+
<code>popover</code>
37+
</Link>{' '}
38+
attribute and gets toggled via{' '}
39+
<Link
40+
target="_blank"
41+
href="https://developer.mozilla.org/en-US/docs/Web/API/Popover_API"
42+
>
43+
popover API
44+
</Link>
45+
. Otherwise, it is rendered as an absolute-positioned element, so
46+
it is recommended to wrap it and its anchor element in a
47+
relative-positioned container.
48+
</p>
49+
</>
50+
}
51+
>
52+
<Library.Pattern>
53+
<Library.Usage symbolName="Popover" />
54+
<Library.Example>
55+
<Library.Demo
56+
title="Basic Popover"
57+
exampleFile="popover-basic"
58+
withSource
59+
/>
60+
</Library.Example>
61+
</Library.Pattern>
62+
63+
<Library.Pattern title="Popover component API">
64+
<Library.Example title="align">
65+
<Library.Info>
66+
<Library.InfoItem label="description">
67+
Determines to what side of the anchor element should the popover
68+
be aligned.
69+
</Library.InfoItem>
70+
<Library.InfoItem label="type">
71+
<code>{"'left' | 'right'"}</code>
72+
</Library.InfoItem>
73+
<Library.InfoItem label="default">
74+
<code>{"'left'"}</code>
75+
</Library.InfoItem>
76+
</Library.Info>
77+
<Library.Demo
78+
title="Right-aligned Popover"
79+
exampleFile="popover-right"
80+
withSource
81+
/>
82+
</Library.Example>
83+
<Library.Example title="anchorElementRef">
84+
<Library.Info>
85+
<Library.InfoItem label="description">
86+
A reference to the element to which the popover should anchor,
87+
which will be used to calculate the popover size and
88+
positioning.
89+
</Library.InfoItem>
90+
<Library.InfoItem label="type">
91+
<code>{'RefObject<HTMLElement | undefined>'}</code>
92+
</Library.InfoItem>
93+
</Library.Info>
94+
</Library.Example>
95+
<Library.Example title="asNativePopover">
96+
<Library.Info>
97+
<Library.InfoItem label="description">
98+
Determines if the{' '}
99+
<Link
100+
target="_blank"
101+
href="https://developer.mozilla.org/en-US/docs/Web/API/Popover_API"
102+
>
103+
popover API
104+
</Link>{' '}
105+
should be used. It{"'"}s mainly used as a test seam, but can be
106+
used to explicitly disable use of the native popover API.
107+
</Library.InfoItem>
108+
<Library.InfoItem label="type">
109+
<code>boolean</code>
110+
</Library.InfoItem>
111+
<Library.InfoItem label="default">
112+
<code>true</code> if the browser supports <code>[popover]</code>
113+
. Otherwise it is <code>false</code>
114+
</Library.InfoItem>
115+
</Library.Info>
116+
</Library.Example>
117+
<Library.Example title="classes">
118+
<Library.Info>
119+
<Library.InfoItem label="description">
120+
Additional CSS classes to pass to the popover.
121+
</Library.InfoItem>
122+
<Library.InfoItem label="type">
123+
<code>string | string[]</code>
124+
</Library.InfoItem>
125+
<Library.InfoItem label="default">
126+
<code>undefined</code>
127+
</Library.InfoItem>
128+
</Library.Info>
129+
</Library.Example>
130+
<Library.Example title="open">
131+
<Library.Info>
132+
<Library.InfoItem label="description">
133+
Whether the <code>Popover</code> is currently open or not.
134+
</Library.InfoItem>
135+
<Library.InfoItem label="type">
136+
<code>boolean</code>
137+
</Library.InfoItem>
138+
</Library.Info>
139+
</Library.Example>
140+
<Library.Example title="restoreFocusOnClose">
141+
<Library.Info>
142+
<Library.InfoItem label="description">
143+
Determines if focus should be restored when the{' '}
144+
<code>Popover</code> is closed.
145+
</Library.InfoItem>
146+
<Library.InfoItem label="type">
147+
<code>boolean</code>
148+
</Library.InfoItem>
149+
<Library.InfoItem label="default">
150+
<code>false</code>
151+
</Library.InfoItem>
152+
</Library.Info>
153+
</Library.Example>
154+
<Library.Example title="variant">
155+
<Library.Info>
156+
<Library.InfoItem label="description">
157+
Set the <code>Popover</code> style variant.
158+
</Library.InfoItem>
159+
<Library.InfoItem label="type">
160+
<code>{"'panel' | 'custom'"}</code>
161+
</Library.InfoItem>
162+
<Library.InfoItem label="default">
163+
<code>{"'panel'"}</code>
164+
</Library.InfoItem>
165+
</Library.Info>
166+
<Library.Demo
167+
title="Custom Popover"
168+
exampleFile="popover-custom"
169+
withSource
170+
/>
171+
</Library.Example>
172+
</Library.Pattern>
173+
</Library.Section>
174+
</Library.Page>
175+
);
176+
}

src/pattern-library/components/patterns/input/SelectPage.tsx

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -126,21 +126,9 @@ export default function SelectPage() {
126126
{"'"} UI can be customized and values can be objects.
127127
</p>
128128
<p>
129-
In browsers that support it, the listbox uses the{' '}
130-
<Link
131-
target="_blank"
132-
href="https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/popover"
133-
>
134-
<code>popover</code>
135-
</Link>{' '}
136-
attribute and gets toggled via{' '}
137-
<Link
138-
target="_blank"
139-
href="https://developer.mozilla.org/en-US/docs/Web/API/Popover_API"
140-
>
141-
popover API
142-
</Link>
143-
. Otherwise, it is rendered as an absolute-positioned element.
129+
A <Library.Link href="/feedback-popover">Popover</Library.Link>{' '}
130+
component is used to wrap the listbox and ensure it is always
131+
correctly positioned.
144132
</p>
145133

146134
<Library.Example title="Composing and styling Selects">
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { useRef, useState } from 'preact/hooks';
2+
3+
import { Popover } from '../../components/feedback';
4+
import { Button } from '../../components/input';
5+
import { useClickAway } from '../../hooks/use-click-away';
6+
7+
export default function App() {
8+
const [open, setOpen] = useState(false);
9+
const buttonRef = useRef<HTMLButtonElement | null>(null);
10+
11+
useClickAway(buttonRef, () => setOpen(false));
12+
13+
return (
14+
<div className="relative flex justify-center">
15+
<Button
16+
variant="primary"
17+
elementRef={buttonRef}
18+
onClick={() => setOpen(prev => !prev)}
19+
>
20+
{open ? 'Close' : 'Open'} Popover
21+
</Button>
22+
<Popover open={open} anchorElementRef={buttonRef} classes="p-2">
23+
The content of the popover goes here
24+
</Popover>
25+
</div>
26+
);
27+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useRef, useState } from 'preact/hooks';
2+
3+
import { Popover } from '../../components/feedback';
4+
import { Button } from '../../components/input';
5+
import { useClickAway } from '../../hooks/use-click-away';
6+
7+
export default function App() {
8+
const [open, setOpen] = useState(false);
9+
const buttonRef = useRef<HTMLButtonElement | null>(null);
10+
11+
useClickAway(buttonRef, () => setOpen(false));
12+
13+
return (
14+
<div className="relative flex justify-center">
15+
<Button
16+
variant="primary"
17+
elementRef={buttonRef}
18+
onClick={() => setOpen(prev => !prev)}
19+
>
20+
{open ? 'Close' : 'Open'} Popover
21+
</Button>
22+
<Popover
23+
open={open}
24+
anchorElementRef={buttonRef}
25+
variant="custom"
26+
classes="p-3 border-4 border-slate-7 bg-green-success text-white font-bold rounded-tr-lg rounded-bl-lg"
27+
>
28+
This popover has been customized
29+
</Popover>
30+
</div>
31+
);
32+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useRef, useState } from 'preact/hooks';
2+
3+
import { Popover } from '../../components/feedback';
4+
import { Button } from '../../components/input';
5+
import { useClickAway } from '../../hooks/use-click-away';
6+
7+
export default function App() {
8+
const [open, setOpen] = useState(false);
9+
const buttonRef = useRef<HTMLButtonElement | null>(null);
10+
11+
useClickAway(buttonRef, () => setOpen(false));
12+
13+
return (
14+
<div className="relative flex justify-center">
15+
<Button
16+
variant="primary"
17+
elementRef={buttonRef}
18+
onClick={() => setOpen(prev => !prev)}
19+
>
20+
{open ? 'Close' : 'Open'} Popover
21+
</Button>
22+
<Popover
23+
open={open}
24+
align="right"
25+
anchorElementRef={buttonRef}
26+
classes="p-2"
27+
>
28+
The content of the popover goes here
29+
</Popover>
30+
</div>
31+
);
32+
}

src/pattern-library/routes.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import TablePage from './components/patterns/data/TablePage';
1313
import ThumbnailPage from './components/patterns/data/ThumbnailPage';
1414
import CalloutPage from './components/patterns/feedback/CalloutPage';
1515
import DialogPage from './components/patterns/feedback/DialogPage';
16+
import PopoverPage from './components/patterns/feedback/PopoverPage';
1617
import SpinnerPage from './components/patterns/feedback/SpinnerPage';
1718
import ToastMessagesPage from './components/patterns/feedback/ToastMessagesPage';
1819
import UseClickAwayPage from './components/patterns/hooks/UseClickAwayPage';
@@ -146,6 +147,12 @@ const routes: PlaygroundRoute[] = [
146147
component: DialogPage,
147148
route: '/feedback-dialog',
148149
},
150+
{
151+
title: 'Popover',
152+
group: 'feedback',
153+
component: PopoverPage,
154+
route: '/feedback-popover',
155+
},
149156
{
150157
title: 'Spinner',
151158
group: 'feedback',

0 commit comments

Comments
 (0)