Skip to content

Commit 56d0e73

Browse files
authored
feat(RAC): add --trigger-width to Popovers used within DialogTrigger (#7869)
* add --trigger-width to Popovers used within DialogTrigger * add story
1 parent 3205f10 commit 56d0e73

File tree

3 files changed

+45
-4
lines changed

3 files changed

+45
-4
lines changed

packages/react-aria-components/docs/Popover.mdx

+8
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,14 @@ A `Popover` can be targeted with the `.react-aria-Popover` CSS selector, or by o
457457

458458
<StateTable properties={docs.exports.PopoverRenderProps.properties} />
459459

460+
Within a DialogTrigger, the popover will have the `data-trigger="DialogTrigger"` attribute. In addition, the `--trigger-width` CSS custom property will be set on the popover, which you can use to make the popover match the width of the trigger button.
461+
462+
```css render=false
463+
.react-aria-Popover[data-trigger=DialogTrigger] {
464+
width: var(--trigger-width);
465+
}
466+
```
467+
460468
### OverlayArrow
461469

462470
A `OverlayArrow` can be targeted with the `.react-aria-OverlayArrow` CSS selector, or by overriding with a custom `className`. It supports the following states and render props:

packages/react-aria-components/src/Dialog.tsx

+17-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
import {AriaDialogProps, useDialog, useId, useOverlayTrigger} from 'react-aria';
1313
import {ButtonContext} from './Button';
1414
import {ContextValue, DEFAULT_SLOT, Provider, SlotProps, StyleProps, useContextProps, useRenderProps} from './utils';
15-
import {filterDOMProps} from '@react-aria/utils';
15+
import {filterDOMProps, useResizeObserver} from '@react-aria/utils';
1616
import {forwardRefType} from '@react-types/shared';
1717
import {HeadingContext} from './RSPContexts';
1818
import {OverlayTriggerProps, OverlayTriggerState, useMenuTriggerState} from 'react-stately';
1919
import {PopoverContext} from './Popover';
2020
import {PressResponder} from '@react-aria/interactions';
21-
import React, {createContext, ForwardedRef, forwardRef, ReactNode, useContext, useRef} from 'react';
21+
import React, {createContext, ForwardedRef, forwardRef, ReactNode, useCallback, useContext, useRef, useState} from 'react';
2222
import {RootMenuTriggerStateContext} from './Menu';
2323

2424
export interface DialogTriggerProps extends OverlayTriggerProps {
@@ -48,6 +48,19 @@ export function DialogTrigger(props: DialogTriggerProps): ReactNode {
4848
let buttonRef = useRef<HTMLButtonElement>(null);
4949
let {triggerProps, overlayProps} = useOverlayTrigger({type: 'dialog'}, state, buttonRef);
5050

51+
// Allows popover width to match trigger element
52+
let [buttonWidth, setButtonWidth] = useState<string | null>(null);
53+
let onResize = useCallback(() => {
54+
if (buttonRef.current) {
55+
setButtonWidth(buttonRef.current.offsetWidth + 'px');
56+
}
57+
}, [buttonRef]);
58+
59+
useResizeObserver({
60+
ref: buttonRef,
61+
onResize: onResize
62+
});
63+
5164
// Label dialog by the trigger as a fallback if there is no title slot.
5265
// This is done in RAC instead of hooks because otherwise we cannot distinguish
5366
// between context and props. Normally aria-labelledby overrides the title
@@ -64,7 +77,8 @@ export function DialogTrigger(props: DialogTriggerProps): ReactNode {
6477
[PopoverContext, {
6578
trigger: 'DialogTrigger',
6679
triggerRef: buttonRef,
67-
'aria-labelledby': overlayProps['aria-labelledby']
80+
'aria-labelledby': overlayProps['aria-labelledby'],
81+
style: {'--trigger-width': buttonWidth} as React.CSSProperties
6882
}]
6983
]}>
7084
<PressResponder {...triggerProps} ref={buttonRef} isPressed={state.isOpen}>

packages/react-aria-components/stories/Popover.stories.tsx

+20-1
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const PopoverExample = () => (
4747
</Dialog>
4848
</Popover>
4949
</DialogTrigger>
50-
);
50+
);
5151

5252

5353
const COUNTDOWN = 5000;
@@ -414,3 +414,22 @@ export const PopoverArrowBoundaryOffsetExample = {
414414
);
415415
}
416416
};
417+
418+
export const PopoverTriggerWidthExample = () => (
419+
<DialogTrigger>
420+
<Button>Open popover</Button>
421+
<Popover
422+
placement="bottom start"
423+
style={{
424+
background: 'Canvas',
425+
color: 'CanvasText',
426+
border: '1px solid gray',
427+
zIndex: 5,
428+
width: 'var(--trigger-width)'
429+
}}>
430+
<Dialog>
431+
Should match the width of the trigger button
432+
</Dialog>
433+
</Popover>
434+
</DialogTrigger>
435+
);

0 commit comments

Comments
 (0)