Skip to content

Commit b301251

Browse files
committed
Add elementRef prop to Popover component
This is a ref for the popover's outermost element, like `elementRef` for other components. This is useful for downstream code which needs information about the geometry of the rendered popover. Downstream code could use a ref on the popover's content instead, but that doesn't include any padding which the popover has.
1 parent 93586dd commit b301251

File tree

3 files changed

+32
-3
lines changed

3 files changed

+32
-3
lines changed

src/components/feedback/Popover.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import classnames from 'classnames';
22
import type { ComponentChildren, JSX, RefObject } from 'preact';
3-
import { useCallback, useEffect, useLayoutEffect, useRef } from 'preact/hooks';
3+
import { useCallback, useEffect, useLayoutEffect } from 'preact/hooks';
44

55
import { useClickAway } from '../../hooks/use-click-away';
66
import { useKeyPress } from '../../hooks/use-key-press';
7+
import { useSyncedRef } from '../../hooks/use-synced-ref';
78
import { ListenerCollection } from '../../util/listener-collection';
9+
import { downcastRef } from '../../util/typing';
810

911
/** Small space to apply between the anchor element and the popover */
1012
const POPOVER_ANCHOR_EL_GAP = '.15rem';
@@ -225,6 +227,9 @@ export type PopoverProps = {
225227
classes?: string | string[];
226228
variant?: 'panel' | 'custom';
227229

230+
/** Ref for the popover element. */
231+
elementRef?: RefObject<HTMLElement>;
232+
228233
/** Whether the popover is currently open or not */
229234
open: boolean;
230235

@@ -328,10 +333,11 @@ export default function Popover({
328333
classes,
329334
variant = 'panel',
330335
onScroll,
336+
elementRef,
331337
/* eslint-disable-next-line no-prototype-builtins */
332338
asNativePopover = HTMLElement.prototype.hasOwnProperty('popover'),
333339
}: PopoverProps) {
334-
const popoverRef = useRef<HTMLDivElement | null>(null);
340+
const popoverRef = useSyncedRef<HTMLElement>(elementRef);
335341

336342
usePopoverPositioning(
337343
anchorElementRef,
@@ -370,7 +376,7 @@ export default function Popover({
370376
},
371377
classes,
372378
)}
373-
ref={popoverRef}
379+
ref={downcastRef(popoverRef)}
374380
popover={asNativePopover && 'auto'}
375381
onScroll={onScroll}
376382
data-testid="popover"

src/components/feedback/test/Popover-test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,4 +440,15 @@ describe('Popover', () => {
440440
});
441441
});
442442
});
443+
444+
it('sets `elementRef`', () => {
445+
const elementRef = { current: null };
446+
const wrapper = createComponent({ elementRef });
447+
448+
assert.instanceOf(elementRef.current, HTMLDivElement);
449+
450+
wrapper.unmount();
451+
452+
assert.isNull(elementRef.current);
453+
});
443454
});

src/pattern-library/components/patterns/feedback/PopoverPage.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,18 @@ export default function PopoverPage() {
127127
</Library.InfoItem>
128128
</Library.Info>
129129
</Library.SectionL3>
130+
<Library.SectionL3 title="elementRef">
131+
<Library.Info>
132+
<Library.InfoItem label="description">
133+
Ref for the popover{"'"}s outermost element.
134+
</Library.InfoItem>
135+
<Library.InfoItem label="type">
136+
<code>
137+
RefObject{'<'}HTMLElement{'>'}
138+
</code>
139+
</Library.InfoItem>
140+
</Library.Info>
141+
</Library.SectionL3>
130142
<Library.SectionL3 title="onClose">
131143
<Library.Info>
132144
<Library.InfoItem label="description">

0 commit comments

Comments
 (0)